Merge branch 'core3-devel'

pull/26/head
Marek Marczykowski-Górecki 7 years ago
commit ad2a976924
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724

@ -4,4 +4,4 @@ language: generic
install: git clone https://github.com/QubesOS/qubes-builder ~/qubes-builder
script: ~/qubes-builder/scripts/travis-build
env:
- DIST_DOM0=fc23 USE_QUBES_REPO_VERSION=3.2 USE_QUBES_REPO_TESTING=1
- DIST_DOM0=fc25 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1

@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Type=Application
Terminal=false
Name=Command Prompt
Comment=Use the command line
Categories=GNOME;GTK;Utility;TerminalEmulator;System;
Exec=cmd /c start cmd

@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Type=Application
Terminal=false
Name=Explorer
Comment=Browse files
Categories=Utility;Core;
Exec=explorer

@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Type=Application
Terminal=false
Name=Internet Explorer
Comment=Browse the Web
Categories=Network;WebBrowser;
Exec=C:\\Program Files\\Internet Explorer\\iexplore.exe

@ -1,10 +0,0 @@
[Desktop Entry]
Version=1.0
Type=Application
Exec=qubes-vm-settings %VMNAME% applications
Icon=qubes-appmenu-select
Terminal=false
Name=%VMNAME%: Add more shortcuts...
GenericName=%VMNAME%: Add more shortcuts...
StartupNotify=false
Categories=System;X-Qubes-VM;

@ -1,10 +0,0 @@
[Desktop Entry]
Version=1.0
Type=Application
Exec=sh -c 'echo firefox | /usr/lib/qubes/qfile-daemon-dvm qubes.VMShell dom0 DEFAULT red'
Icon=dispvm-red
Terminal=false
Name=DispVM: Firefox web browser
GenericName=DispVM: Web browser
StartupNotify=false
Categories=Network;X-Qubes-VM;

@ -1,10 +0,0 @@
[Desktop Entry]
Version=1.0
Type=Application
Exec=sh -c 'echo xterm | /usr/lib/qubes/qfile-daemon-dvm qubes.VMShell dom0 DEFAULT red'
Icon=dispvm-red
Terminal=false
Name=DispVM: xterm
GenericName=DispVM: Terminal
StartupNotify=false
Categories=Network;X-Qubes-VM;

@ -1,5 +0,0 @@
[Desktop Entry]
Encoding=UTF-8
Type=Directory
Name=DisposableVM
Icon=dispvm-red

@ -1,5 +0,0 @@
[Desktop Entry]
Encoding=UTF-8
Type=Directory
Name=ServiceVM: %VMNAME%
Icon=%XDGICON%

@ -1,10 +0,0 @@
[Desktop Entry]
Version=1.0
Type=Application
Exec=qvm-start --quiet --tray %VMNAME%
Icon=%XDGICON%
Terminal=false
Name=%VMNAME%: Start
GenericName=%VMNAME%: Start
StartupNotify=false
Categories=System;X-Qubes-VM;

@ -1,5 +0,0 @@
[Desktop Entry]
Encoding=UTF-8
Type=Directory
Name=Template: %VMNAME%
Icon=%XDGICON%

@ -1,5 +0,0 @@
[Desktop Entry]
Encoding=UTF-8
Type=Directory
Name=Domain: %VMNAME%
Icon=%XDGICON%

@ -1,20 +0,0 @@
#!/bin/sh
SRC=$1
DSTDIR=$2
VMNAME=$3
VMDIR=$4
XDGICON=$5
DST=$DSTDIR/$VMNAME-$(basename $SRC)
if ! [ -r "$SRC" ]; then
exit 0
fi
sed \
-e "s/%VMNAME%/$VMNAME/" \
-e "s %VMDIR% $VMDIR " \
-e "s/%XDGICON%/$XDGICON/" \
<$SRC >$DST

@ -1,14 +0,0 @@
#!/bin/sh
SRC=$1
DST=$2
VMNAME=$3
VMDIR=$4
XDGICON=$5
sed \
-e "s/%VMNAME%/$VMNAME/" \
-e "s %VMDIR% $VMDIR " \
-e "s/%XDGICON%/$XDGICON/" \
<$SRC >$DST

@ -1,64 +0,0 @@
#!/bin/sh
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
#
# 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.
#
#
SRCDIR=$1
VMNAME=$2
VMTYPE=$3
if [ -z "$VMTYPE" ]; then
VMTYPE=appvms
fi
XDGICON=$4
VMDIR=/var/lib/qubes/$VMTYPE/$VMNAME
APPSDIR=$VMDIR/apps
if [ $# -lt 2 ]; then
echo "usage: $0 <apps_templates_dir> <vmname> [appvms|vm-templates|servicevms]"
exit
fi
mkdir -p $APPSDIR
if [ "$SRCDIR" != "none" ]; then
echo "--> Converting Appmenu Templates..."
if [ -r "$VMDIR/whitelisted-appmenus.list" ]; then
cat $VMDIR/whitelisted-appmenus.list | xargs -I{} /usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh $SRCDIR/{} $APPSDIR $VMNAME $VMDIR $XDGICON
else
find $SRCDIR -name "*.desktop" $CHECK_WHITELISTED -exec /usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh {} $APPSDIR $VMNAME $VMDIR $XDGICON \;
fi
/usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh /usr/share/qubes-appmenus/qubes-appmenu-select.desktop $APPSDIR $VMNAME $VMDIR $XDGICON
if [ "$VMTYPE" = "vm-templates" ]; then
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-templatevm.directory.template
elif [ "$VMTYPE" = "servicevms" ]; then
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-servicevm.directory.template
else
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-vm.directory.template
fi
/usr/libexec/qubes-appmenus/convert-dirtemplate2vm.sh $DIR_TEMPLATE $APPSDIR/$VMNAME-vm.directory $VMNAME $VMDIR $XDGICON
fi
echo "--> Adding Apps to the Menu..."
LC_COLLATE=C xdg-desktop-menu install --noupdate $APPSDIR/*.directory $APPSDIR/*.desktop
if [ -n "$KDE_SESSION_UID" -a -z "$SKIP_CACHE_REBUILD" ]; then
xdg-desktop-menu forceupdate
kbuildsycoca$KDE_SESSION_VERSION
fi

@ -1,394 +0,0 @@
#!/usr/bin/python2
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
#
# 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 sys
import os
import os.path
import shutil
import dbus
from qubes.qubes import QubesVm, QubesHVm
from qubes.qubes import vm_files, system_path
import qubes.imgconverter
vm_files['appmenus_templates_subdir'] = 'apps.templates'
vm_files['appmenus_template_icons_subdir'] = 'apps.tempicons'
vm_files['appmenus_subdir'] = 'apps'
vm_files['appmenus_icons_subdir'] = 'apps.icons'
vm_files['appmenus_template_templates_subdir'] = 'apps-template.templates'
vm_files['appmenus_whitelist'] = 'whitelisted-appmenus.list'
system_path['appmenu_start_hvm_template'] = \
'/usr/share/qubes-appmenus/qubes-start.desktop'
system_path['appmenu_create_cmd'] = \
'/usr/libexec/qubes-appmenus/create-apps-for-appvm.sh'
system_path['appmenu_remove_cmd'] = \
'/usr/libexec/qubes-appmenus/remove-appvm-appmenus.sh'
def QubesVm_get_appmenus_templates_dir(self):
if self.updateable:
return self.absolute_path(vm_files["appmenus_templates_subdir"], None)
elif self.template is not None:
return self.template.appmenus_templates_dir
else:
return None
QubesVm.appmenus_templates_dir = property(QubesVm_get_appmenus_templates_dir)
def QubesVm_get_appmenus_template_icons_dir(self):
if self.updateable:
return self.absolute_path(vm_files["appmenus_template_icons_subdir"],
None)
elif self.template:
return self.template.appmenus_template_icons_dir
else:
return None
QubesVm.appmenus_template_icons_dir = \
property(QubesVm_get_appmenus_template_icons_dir)
def QubesVm_get_appmenus_dir(self):
return self.absolute_path(vm_files["appmenus_subdir"], None)
QubesVm.appmenus_dir = property(QubesVm_get_appmenus_dir)
def QubesVm_get_appmenus_icons_dir(self):
return self.absolute_path(vm_files["appmenus_icons_subdir"], None)
QubesVm.appmenus_icons_dir = property(QubesVm_get_appmenus_icons_dir)
def get_whitelist_names(vm):
return (whitelist for whitelist in (
vm_files["appmenus_whitelist"],
'vm-' + vm_files["appmenus_whitelist"],
'netvm-' + vm_files["appmenus_whitelist"])
if os.path.exists(os.path.join(vm.dir_path, whitelist)))
def QubesVm_appmenus_create(self, verbose=False, source_template=None):
if source_template is None:
source_template = self.template
if self.internal:
return
if self.is_disposablevm():
return
if self.is_netvm():
vmtype = 'servicevms'
elif self.is_template():
vmtype = 'vm-templates'
else:
vmtype = 'appvms'
try:
msgoutput = None if verbose else open(os.devnull, 'w')
if source_template is not None:
subprocess.check_call([system_path["appmenu_create_cmd"],
source_template.appmenus_templates_dir,
self.name, vmtype, self.label.icon],
stdout=msgoutput, stderr=msgoutput)
elif self.appmenus_templates_dir is not None:
subprocess.check_call([system_path["appmenu_create_cmd"],
self.appmenus_templates_dir, self.name,
vmtype, self.label.icon],
stdout=msgoutput, stderr=msgoutput)
else:
# Only add apps to menu
subprocess.check_call([system_path["appmenu_create_cmd"],
"none", self.name, vmtype,
self.label.icon],
stdout=msgoutput, stderr=msgoutput)
except subprocess.CalledProcessError:
print >> sys.stderr, "Ooops, there was a problem creating appmenus " \
"for {0} VM!".format(self.name)
def QubesVm_appmenus_remove(self):
if self.is_netvm():
vmtype = 'servicevms'
elif self.is_template():
vmtype = 'vm-templates'
else:
vmtype = 'appvms'
subprocess.check_call([system_path["appmenu_remove_cmd"], self.name,
vmtype], stderr=open(os.devnull, 'w'))
def QubesVm_appmenus_cleanup(self):
srcdir = self.appmenus_templates_dir
if srcdir is None:
return
if not os.path.exists(srcdir):
return
if not os.path.exists(self.appmenus_dir):
return
for appmenu in os.listdir(self.appmenus_dir):
if not os.path.exists(os.path.join(srcdir, appmenu)):
os.unlink(os.path.join(self.appmenus_dir, appmenu))
def QubesVm_appmenus_replace_entry(self, old_name, new_name):
for whitelist in get_whitelist_names(self):
whitelist_path = os.path.join(self.dir_path, whitelist)
with open(whitelist_path) as f:
old_lines = f.readlines()
new_lines = [
(new_name + '\n' if l == old_name + '\n' else l)
for l in old_lines]
if new_lines != old_lines:
with open(whitelist_path, 'w') as f:
f.write(''.join(new_lines))
def QubesVm_appicons_create(self, srcdir=None, force=False):
if srcdir is None:
srcdir = self.appmenus_template_icons_dir
if srcdir is None:
return
if not os.path.exists(srcdir):
return
if self.internal:
return
if self.is_disposablevm():
return
whitelist = os.path.join(self.dir_path, vm_files['appmenus_whitelist'])
if os.path.exists(whitelist):
whitelist = [line.strip() for line in open(whitelist)]
else:
whitelist = None
if not os.path.exists(self.appmenus_icons_dir):
os.mkdir(self.appmenus_icons_dir)
elif not os.path.isdir(self.appmenus_icons_dir):
os.unlink(self.appmenus_icons_dir)
os.mkdir(self.appmenus_icons_dir)
for icon in os.listdir(srcdir):
desktop = os.path.splitext(icon)[0] + '.desktop'
if whitelist and desktop not in whitelist:
continue
src_icon = os.path.join(srcdir, icon)
dst_icon = os.path.join(self.appmenus_icons_dir, icon)
if not os.path.exists(dst_icon) or force or \
os.path.getmtime(src_icon) > os.path.getmtime(dst_icon):
qubes.imgconverter.tint(src_icon, dst_icon, self.label.color)
def QubesVm_appicons_remove(self):
if not os.path.exists(self.appmenus_icons_dir):
return
for icon in os.listdir(self.appmenus_icons_dir):
os.unlink(os.path.join(self.appmenus_icons_dir, icon))
def QubesVm_appicons_cleanup(self):
srcdir = self.appmenus_template_icons_dir
if srcdir is None:
return
if not os.path.exists(srcdir):
return
if not os.path.exists(self.appmenus_icons_dir):
return
for icon in os.listdir(self.appmenus_icons_dir):
if not os.path.exists(os.path.join(srcdir, icon)):
os.unlink(os.path.join(self.appmenus_icons_dir, icon))
def QubesVm_pre_rename(self, new_name):
self.appmenus_remove()
def QubesVm_post_rename(self, old_name):
self.appmenus_create()
def QubesVm_create_on_disk(self, verbose, source_template):
if isinstance(self, QubesHVm) and source_template is None:
if verbose:
print >> sys.stderr, "--> Creating appmenus directory: {0}".format(
self.appmenus_templates_dir)
os.mkdir(self.appmenus_templates_dir)
shutil.copy(system_path["appmenu_start_hvm_template"],
self.appmenus_templates_dir)
source_whitelist_filename = 'vm-' + vm_files["appmenus_whitelist"]
if self.is_netvm():
source_whitelist_filename = 'netvm-' + vm_files["appmenus_whitelist"]
if source_template and os.path.exists(
os.path.join(source_template.dir_path, source_whitelist_filename)):
if verbose:
print >> sys.stderr, "--> Creating default whitelisted apps list: {0}". \
format(self.dir_path + '/' + vm_files["whitelisted_appmenus"])
shutil.copy(
os.path.join(source_template.dir_path, source_whitelist_filename),
os.path.join(self.dir_path, vm_files["whitelisted_appmenus"]))
if source_template and self.updateable:
if verbose:
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}". \
format(source_template.appmenus_templates_dir,
self.appmenus_templates_dir)
if os.path.isdir(source_template.appmenus_templates_dir):
shutil.copytree(source_template.appmenus_templates_dir,
self.appmenus_templates_dir)
else:
os.mkdir(self.appmenus_templates_dir)
if os.path.isdir(source_template.appmenus_template_icons_dir):
shutil.copytree(source_template.appmenus_template_icons_dir,
self.appmenus_template_icons_dir)
else:
os.mkdir(self.appmenus_template_icons_dir)
# Create appmenus
self.appicons_create()
self.appmenus_create(verbose=verbose)
def QubesVm_clone_disk_files(self, src_vm, verbose):
if src_vm.updateable and src_vm.appmenus_templates_dir is not None and \
self.appmenus_templates_dir is not None:
if verbose:
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}". \
format(src_vm.appmenus_templates_dir,
self.appmenus_templates_dir)
shutil.copytree(src_vm.appmenus_templates_dir,
self.appmenus_templates_dir)
if src_vm.updateable and src_vm.appmenus_template_icons_dir is not None \
and self.appmenus_template_icons_dir is not None and \
os.path.isdir(src_vm.appmenus_template_icons_dir):
if verbose:
print >> sys.stderr, "--> Copying the template's appmenus " \
"template icons dir:\n{0} ==>\n{1}". \
format(src_vm.appmenus_template_icons_dir,
self.appmenus_template_icons_dir)
shutil.copytree(src_vm.appmenus_template_icons_dir,
self.appmenus_template_icons_dir)
for whitelist in get_whitelist_names(src_vm):
if verbose:
print >> sys.stderr, "--> Copying whitelisted apps list: {0}". \
format(whitelist)
shutil.copy(os.path.join(src_vm.dir_path, whitelist),
os.path.join(self.dir_path, whitelist))
# Create appmenus
self.appicons_create()
self.appmenus_create(verbose=verbose)
def QubesVm_remove_from_disk(self):
self.appmenus_remove()
def QubesVm_label_setter(self, _):
self.appicons_create(force=True)
# Apparently desktop environments heavily caches the icons,
# see #751 for details
if "plasma" in os.environ.get("DESKTOP_SESSION", ""):
try:
os.unlink(os.path.expandvars(
"$HOME/.kde/cache-$HOSTNAME/icon-cache.kcache"))
except:
pass
try:
notify_object = dbus.SessionBus().get_object(
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications")
notify_object.Notify(
"Qubes", 0, self.label.icon, "Qubes",
"You will need to log off and log in again for the VM icons "
"to update in the KDE launcher menu",
[], [], 10000,
dbus_interface="org.freedesktop.Notifications")
except:
pass
elif "xfce" in os.environ.get("DESKTOP_SESSION", ""):
self.appmenus_remove()
self.appmenus_create()
def QubesVm_appmenus_recreate(self):
"""
Force recreation of all appmenus and icons. For example when VM label
color was changed
"""
self.appmenus_remove()
self.appmenus_cleanup()
self.appicons_remove()
self.appicons_create()
self.appmenus_create()
def QubesVm_appmenus_update(self):
"""
Similar to appmenus_recreate, but do not touch unchanged files
"""
self.appmenus_remove()
self.appmenus_cleanup()
self.appicons_create()
self.appicons_cleanup()
self.appmenus_create()
def QubesVm_set_attr(self, name, newvalue, oldvalue):
if name == 'internal':
if newvalue and not oldvalue:
self.appmenus_remove()
elif not newvalue and oldvalue:
self.appmenus_create()
# new methods
QubesVm.appmenus_create = QubesVm_appmenus_create
QubesVm.appmenus_remove = QubesVm_appmenus_remove
QubesVm.appmenus_cleanup = QubesVm_appmenus_cleanup
QubesVm.appmenus_recreate = QubesVm_appmenus_recreate
QubesVm.appmenus_update = QubesVm_appmenus_update
QubesVm.appmenus_replace_entry = QubesVm_appmenus_replace_entry
QubesVm.appicons_create = QubesVm_appicons_create
QubesVm.appicons_cleanup = QubesVm_appicons_cleanup
QubesVm.appicons_remove = QubesVm_appicons_remove
# hooks for existing methods
QubesVm.hooks_pre_rename.append(QubesVm_pre_rename)
QubesVm.hooks_post_rename.append(QubesVm_post_rename)
QubesVm.hooks_create_on_disk.append(QubesVm_create_on_disk)
QubesVm.hooks_clone_disk_files.append(QubesVm_clone_disk_files)
QubesVm.hooks_remove_from_disk.append(QubesVm_remove_from_disk)
QubesVm.hooks_label_setter.append(QubesVm_label_setter)
QubesVm.hooks_set_attr.append(QubesVm_set_attr)

@ -1,392 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2011 Marek Marczykowski <marmarek@mimuw.edu.pl>
#
# 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 optparse
import subprocess
import re
import os
import sys
import shutil
import pipes
from optparse import OptionParser
from qubes.qubes import QubesVmCollection, QubesException, system_path
from qubes.qubes import QubesHVm
from qubes.qubes import vm_files
import qubes.imgconverter
from qubes.qubes import vmm
# 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()_%&>/{}\"'\\:.= -]*$"),
"Icon": re.compile(r"^[a-zA-Z0-9/_.-]*$"),
}
CATEGORIES_WHITELIST = {
# Main Categories
# http://standards.freedesktop.org/menu-spec/1.1/apa.html 20140507
'AudioVideo', 'Audio', 'Video', 'Development', 'Education', 'Game',
'Graphics', 'Network', 'Office', 'Science', 'Settings', 'System',
'Utility',
# Additional Categories
# http://standards.freedesktop.org/menu-spec/1.1/apas02.html
'Building', 'Debugger', 'IDE', 'GUIDesigner', 'Profiling',
'RevisionControl', 'Translation', 'Calendar', 'ContactManagement',
'Database', 'Dictionary', 'Chart', 'Email', 'Finance', 'FlowChart', 'PDA',
'ProjectManagement', 'Presentation', 'Spreadsheet', 'WordProcessor',
'2DGraphics', 'VectorGraphics', 'RasterGraphics', '3DGraphics', 'Scanning',
'OCR', 'Photography', 'Publishing', 'Viewer', 'TextTools',
'DesktopSettings', 'HardwareSettings', 'Printing', 'PackageManager',
'Dialup', 'InstantMessaging', 'Chat', 'IRCClient', 'Feed', 'FileTransfer',
'HamRadio', 'News', 'P2P', 'RemoteAccess', 'Telephony', 'TelephonyTools',
'VideoConference', 'WebBrowser', 'WebDevelopment', 'Midi', 'Mixer',
'Sequencer', 'Tuner', 'TV', 'AudioVideoEditing', 'Player', 'Recorder',
'DiscBurning', 'ActionGame', 'AdventureGame', 'ArcadeGame', 'BoardGame',
'BlocksGame', 'CardGame', 'KidsGame', 'LogicGame', 'RolePlaying',
'Shooter', 'Simulation', 'SportsGame', 'StrategyGame', 'Art',
'Construction', 'Music', 'Languages', 'ArtificialIntelligence',
'Astronomy', 'Biology', 'Chemistry', 'ComputerScience',
'DataVisualization', 'Economy', 'Electricity', 'Geography', 'Geology',
'Geoscience', 'History', 'Humanities', 'ImageProcessing', 'Literature',
'Maps', 'Math', 'NumericalAnalysis', 'MedicalSoftware', 'Physics',
'Robotics', 'Spirituality', 'Sports', 'ParallelComputing', 'Amusement',
'Archiving', 'Compression', 'Electronics', 'Emulator', 'Engineering',
'FileTools', 'FileManager', 'TerminalEmulator', 'Filesystem', 'Monitor',
'Security', 'Accessibility', 'Calculator', 'Clock', 'TextEditor',
'Documentation', 'Adult', 'Core', 'KDE', 'GNOME', 'XFCE', 'GTK', 'Qt',
'Motif', 'Java', 'ConsoleOnly',
# Reserved Categories (not whitelisted)
# http://standards.freedesktop.org/menu-spec/1.1/apas03.html
# 'Screensaver', 'TrayIcon', 'Applet', 'Shell',
}
def sanitise_categories(untrusted_value):
untrusted_categories = (c.strip() for c in untrusted_value.split(';') if c)
categories = (c for c in untrusted_categories if c in CATEGORIES_WHITELIST)
return ';'.join(categories) + ';'
def fallback_hvm_appmenulist():
p = subprocess.Popen(["grep", "-rH", "=", "/usr/share/qubes-appmenus/hvm"],
stdout=subprocess.PIPE)
(stdout, stderr) = p.communicate()
return stdout.splitlines()
def get_appmenus(vm):
global appmenus_line_count
global appmenus_line_size
untrusted_appmenulist = []
if vm is None:
while appmenus_line_count > 0:
untrusted_line = sys.stdin.readline(appmenus_line_size)
if untrusted_line == "":
break
untrusted_appmenulist.append(untrusted_line.strip())
appmenus_line_count -= 1
if appmenus_line_count == 0:
raise QubesException("Line count limit exceeded")
else:
p = vm.run('QUBESRPC qubes.GetAppmenus dom0', passio_popen=True,
gui=False)
while appmenus_line_count > 0:
untrusted_line = p.stdout.readline(appmenus_line_size)
if untrusted_line == "":
break
untrusted_appmenulist.append(untrusted_line.strip())
appmenus_line_count -= 1
p.wait()
if p.returncode != 0:
if isinstance(vm, QubesHVm):
untrusted_appmenulist = fallback_hvm_appmenulist()
else:
raise QubesException("Error getting application list")
if appmenus_line_count == 0:
raise QubesException("Line count limit exceeded")
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:
filename = untrusted_m.group(1)
assert '/' not in filename
assert '\0' not in filename
untrusted_key = untrusted_m.group(2)
assert '\0' not in untrusted_key
assert '\x1b' not in untrusted_key
assert '=' not in untrusted_key
untrusted_value = untrusted_m.group(3)
# TODO add key-dependent asserts
# Look only at predefined keys
if untrusted_key in fields_regexp:
if fields_regexp[untrusted_key].match(untrusted_value):
# now values are sanitized
key = untrusted_key
if key == 'Categories':
value = sanitise_categories(untrusted_value)
else:
value = untrusted_value
if filename not in appmenus:
appmenus[filename] = {}
appmenus[filename][key] = value
else:
print >> sys.stderr, \
"Warning: ignoring key %r of %s" % \
(untrusted_key, filename)
# else: ignore this key
return appmenus
def create_template(path, values):
# check if all required fields are present
for key in required_fields:
if key not in values:
print >> sys.stderr, "Warning: not creating/updating '%s' " \
"because of missing '%s' key" % (
path, key)
return
desktop_entry = ""
desktop_entry += "[Desktop Entry]\n"
desktop_entry += "Version=1.0\n"
desktop_entry += "Type=Application\n"
desktop_entry += "Terminal=false\n"
desktop_entry += "X-Qubes-VmName=%VMNAME%\n"
if 'Icon' in values:
icon_file = os.path.splitext(os.path.split(path)[1])[0] + '.png'
desktop_entry += "Icon={0}\n".format(os.path.join(
'%VMDIR%', vm_files['appmenus_icons_subdir'], icon_file))
else:
desktop_entry += "Icon=%XDGICON%\n"
for key in ["Name", "GenericName"]:
if key in values:
desktop_entry += "{0}=%VMNAME%: {1}\n".format(key, values[key])
# force category X-Qubes-VM
values["Categories"] = values.get("Categories", "") + "X-Qubes-VM;"
for key in ["Comment", "Categories"]:
if key in values:
desktop_entry += "{0}={1}\n".format(key, values[key])
desktop_entry += "Exec=qvm-run -q --tray -a %VMNAME% -- {0}\n".format(
pipes.quote(values['Exec']))
if not os.path.exists(path) or desktop_entry != open(path, "r").read():
desktop_file = open(path, "w")
desktop_file.write(desktop_entry)
desktop_file.close()
def main():
env_vmname = os.environ.get("QREXEC_REMOTE_DOMAIN")
usage = "usage: %prog [options] <vm-name>\n" \
"Update 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")
parser.add_option("--force-rpc", action="store_true", dest="force_rpc",
default=False,
help="Force to start a new RPC call, "
"even if called from existing one")
# Do not use any RPC call, expects data on stdin (in qubes.GetAppmenus
# format)
parser.add_option("--offline-mode", dest="offline_mode",
action="store_true", default=False,
help=optparse.SUPPRESS_HELP)
(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 into " \
"permissions problems."
print >> sys.stderr, "Retry as unprivileged user."
print >> sys.stderr, "... or use --force-root to continue anyway."
exit(1)
if options.offline_mode:
vmm.offline_mode = True
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 options.offline_mode and not vm.is_running():
print >> sys.stderr, "ERROR: Appmenus can be retrieved only from " \
"running VM - start it first"
exit(1)
if not options.offline_mode and env_vmname is None or options.force_rpc:
new_appmenus = get_appmenus(vm)
else:
options.verbose = False
new_appmenus = get_appmenus(None)
if len(new_appmenus) == 0:
print >> sys.stderr, "ERROR: No appmenus received, terminating"
exit(1)
os.umask(002)
if not os.path.exists(vm.appmenus_templates_dir):
os.mkdir(vm.appmenus_templates_dir)
if not os.path.exists(vm.appmenus_template_icons_dir):
os.mkdir(vm.appmenus_template_icons_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(
os.path.join(vm.appmenus_templates_dir, appmenu_file)):
print >> sys.stderr, "---> Updating {0}".format(appmenu_file)
else:
print >> sys.stderr, "---> Creating {0}".format(appmenu_file)
# TODO: icons support in offline mode
if options.offline_mode:
new_appmenus[appmenu_file].pop('Icon', None)
if 'Icon' in new_appmenus[appmenu_file]:
# the following line is used for time comparison
icondest = os.path.join(vm.appmenus_template_icons_dir,
os.path.splitext(appmenu_file)[0] + '.png')
try:
icon = qubes.imgconverter.Image. \
get_xdg_icon_from_vm(vm,
new_appmenus[
appmenu_file][
'Icon'])
if os.path.exists(icondest):
old_icon = qubes.imgconverter.Image.load_from_file(icondest)
else:
old_icon = None
if old_icon is None or icon != old_icon:
icon.save(icondest)
except Exception, e:
print >> sys.stderr, '----> Failed to get icon for {0}: {1!s}'.\
format(appmenu_file, e)
if os.path.exists(icondest):
print >> sys.stderr, '-----> Found old icon, ' \
'using it instead'
else:
del new_appmenus[appmenu_file]['Icon']
create_template(os.path.join(vm.appmenus_templates_dir, appmenu_file),
new_appmenus[appmenu_file])
# Delete appmenus of removed applications
if options.verbose:
print >> sys.stderr, "--> Cleaning old files"
for appmenu_file in os.listdir(vm.appmenus_templates_dir):
if not appmenu_file.endswith('.desktop'):
continue
if appmenu_file not in new_appmenus:
if options.verbose:
print >> sys.stderr, "---> Removing {0}".format(appmenu_file)
os.unlink(os.path.join(vm.appmenus_templates_dir, appmenu_file))
if isinstance(vm, QubesHVm):
if not os.path.exists(os.path.join(vm.appmenus_templates_dir,
os.path.basename(system_path[
'appmenu_start_hvm_template']))):
shutil.copy(system_path['appmenu_start_hvm_template'],
vm.appmenus_templates_dir)
vm.appmenus_update()
if hasattr(vm, 'appvms'):
os.putenv('SKIP_CACHE_REBUILD', '1')
for child_vm in vm.appvms.values():
try:
child_vm.appmenus_update()
except Exception, e:
print >> sys.stderr, "---> Failed to recreate appmenus for " \
"'{0}': {1}".format(child_vm.name, str(e))
subprocess.call(['xdg-desktop-menu', 'forceupdate'])
if 'KDE_SESSION_UID' in os.environ:
subprocess.call(['kbuildsycoca' + os.environ.get('KDE_SESSION_VERSION', '4')])
os.unsetenv('SKIP_CACHE_REBUILD')
main()

@ -1 +0,0 @@
/usr/libexec/qubes-appmenus/qubes-receive-appmenus

@ -1,56 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# The Qubes OS Project, http://www.qubes-os.org
#
# 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 sys
from qubes.qubes import QubesVmCollection
def main():
if len(sys.argv) != 4:
print >> sys.stderr, \
'Usage: qvm-appmenu-replace VM_NAME OLD_NAME.desktop NEW_NAME.desktop'
sys.exit(1)
vm_name = sys.argv[1]
old_name = sys.argv[2]
new_name = sys.argv[3]
qvm_collection = QubesVmCollection()
qvm_collection.lock_db_for_reading()
qvm_collection.load()
qvm_collection.unlock_db()
vm = qvm_collection.get_vm_by_name(vm_name)
if vm is None:
print >> sys.stderr, "ERROR: A VM with the name '{0}' " \
"does not exist in the system.".format(
vm_name)
exit(1)
if vm.template is not None:
print >> sys.stderr, "ERROR: To replace appmenu for template based VM, " \
"do it on template instead"
exit(1)
vm.appmenus_replace_entry(old_name, new_name)
if hasattr(vm, 'appvms'):
for child_vm in vm.appvms.values():
child_vm.appmenus_replace_entry(old_name, new_name)
main()

@ -1,2 +0,0 @@
#!/bin/sh
exec /usr/libexec/qubes-appmenus/qubes-receive-appmenus $@

@ -1,23 +0,0 @@
#!/bin/sh
VMNAME=$1
VMTYPE=$2
if [ -z "$VMTYPE" ]; then
VMTYPE=appvms
fi
VMDIR=/var/lib/qubes/$VMTYPE/$VMNAME
APPSDIR=$VMDIR/apps
if [ $# -lt 1 ]; then
echo "usage: $0 <vmname> [appvms|vm-templates|servicevms]"
exit
fi
if ls $APPSDIR/*.directory $APPSDIR/*.desktop > /dev/null 2>&1; then
LC_COLLATE=C xdg-desktop-menu uninstall $APPSDIR/*.directory $APPSDIR/*.desktop
rm -f $APPSDIR/*.desktop $APPSDIR/*.directory
rm -f $HOME/.config/menus/applications-merged/user-$VMNAME-vm.menu
fi
if [ -n "$KDE_SESSION_UID" -a -z "$SKIP_CACHE_REBUILD" ]; then
kbuildsycoca$KDE_SESSION_VERSION
fi

@ -1,6 +1,6 @@
#!/bin/bash
UPDATEVM=`qubes-prefs --get updatevm`
UPDATEVM=`qubes-prefs --force-root updatevm`
UPDATES_STAT_FILE=/var/lib/qubes/updates/dom0-updates-available
if [ -z "$UPDATEVM" ]; then

@ -1,4 +1,4 @@
#!/usr/bin/python2
#!/usr/bin/python3
#
# The Qubes OS Project, http://www.qubes-os.org
#
@ -18,16 +18,14 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
import os
import os.path
import re
import sys
import subprocess
import shutil
import glob
import grp
from qubes.qubes import QubesVmCollection
import qubes
updates_dir = "/var/lib/qubes/updates"
updates_rpm_dir = updates_dir + "/rpm"
@ -48,20 +46,23 @@ package_regex = re.compile(r"^[A-Za-z0-9._+-]{1,128}.rpm$")
# .....rpm: RSA sha1 ((MD5) PGP) md5 NOT OK (MISSING KEYS: (MD5) PGP#246110c1)
gpg_ok_regex = re.compile(r": [a-z0-9() ]* (pgp|gpg) [a-z0-9 ]*OK$")
def dom0updates_fatal(pkg, msg):
global updates_error_file_handle
print >> sys.stderr, msg
print(msg, file=sys.stderr)
if updates_error_file_handle is None:
updates_error_file_handle = open(updates_error_file, "a")
updates_error_file_handle.write(msg + "\n")
os.remove(pkg)
def handle_dom0updates(updatevm):
global updates_error_file_handle
source=os.getenv("QREXEC_REMOTE_DOMAIN")
source = os.getenv("QREXEC_REMOTE_DOMAIN")
if source != updatevm.name:
print >> sys.stderr, 'Domain ' + str(source) + ' not allowed to send dom0 updates'
print('Domain ' + str(source) + ' not allowed to send dom0 updates',
file=sys.stderr)
exit(1)
# Clean old packages
if os.path.exists(updates_rpm_dir):
@ -72,15 +73,17 @@ def handle_dom0updates(updatevm):
os.remove(updates_error_file)
os.environ['LC_ALL'] = 'C'
qubes_gid = grp.getgrnam('qubes').gr_gid
old_umask = os.umask(002)
old_umask = os.umask(0o002)
os.mkdir(updates_rpm_dir)
os.chown(updates_rpm_dir, -1, qubes_gid)
os.chmod(updates_rpm_dir, 0775)
subprocess.check_call(["/usr/libexec/qubes/qfile-dom0-unpacker", str(os.getuid()), updates_rpm_dir])
os.chmod(updates_rpm_dir, 0o0775)
subprocess.check_call(["/usr/libexec/qubes/qfile-dom0-unpacker",
str(os.getuid()), updates_rpm_dir])
# Verify received files
for untrusted_f in os.listdir(updates_rpm_dir):
if not package_regex.match(untrusted_f):
dom0updates_fatal(updates_rpm_dir + '/' + untrusted_f, 'Domain ' + source + ' sent unexpected file: ' + untrusted_f)
dom0updates_fatal(updates_rpm_dir + '/' + untrusted_f,
'Domain ' + source + ' sent unexpected file: ' + untrusted_f)
else:
f = untrusted_f
assert '/' not in f
@ -89,14 +92,17 @@ def handle_dom0updates(updatevm):
full_path = updates_rpm_dir + "/" + f
if os.path.islink(full_path) or not os.path.isfile(full_path):
dom0updates_fatal(full_path, 'Domain ' + source + ' sent not regular file')
p = subprocess.Popen (["/bin/rpm", "-K", full_path],
dom0updates_fatal(
full_path, 'Domain ' + source + ' sent not regular file')
p = subprocess.Popen(["/bin/rpm", "-K", full_path],
stdout=subprocess.PIPE)
output = p.communicate()[0]
output = p.communicate()[0].decode('ascii')
if p.returncode != 0:
dom0updates_fatal(full_path, 'Error while verifing %s signature: %s' % (f, output))
dom0updates_fatal(full_path,
'Error while verifing %s signature: %s' % (f, output))
if not gpg_ok_regex.search(output.strip()):
dom0updates_fatal(full_path, 'Domain ' + source + ' sent not signed rpm: ' + f)
dom0updates_fatal(full_path,
'Domain ' + source + ' sent not signed rpm: ' + f)
if updates_error_file_handle is not None:
updates_error_file_handle.close()
# After updates received - create repo metadata
@ -106,27 +112,28 @@ def handle_dom0updates(updatevm):
createrepo_cmd += ["-q", updates_dir]
subprocess.check_call(createrepo_cmd)
os.chown(updates_repodata_dir, -1, qubes_gid)
os.chmod(updates_repodata_dir, 0775)
os.chmod(updates_repodata_dir, 0o0775)
# Clean old cache
subprocess.call(["sudo", "/usr/bin/yum", "-q", "clean", "all"], stdout=sys.stderr)
# This will fail because of "smart" detection of no-network, but it will invalidate the cache
subprocess.call(["sudo", "/usr/bin/yum", "-q", "clean", "all"],
stdout=sys.stderr)
# This will fail because of "smart" detection of no-network,
# but it will invalidate the cache
try:
null = open('/dev/null','w')
null = open('/dev/null', 'w')
subprocess.call(["/usr/bin/pkcon", "refresh"], stdout=null)
null.close()
except:
except subprocess.CalledProcessError:
pass
os.umask(old_umask)
exit(0)
def main():
qvm_collection = QubesVmCollection()
qvm_collection.lock_db_for_reading()
qvm_collection.load()
qvm_collection.unlock_db()
def main():
app = qubes.Qubes()
updatevm = qvm_collection.get_updatevm_vm()
updatevm = app.updatevm
if updatevm is None:
exit(1)
handle_dom0updates(updatevm)
main()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

@ -1,10 +0,0 @@
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This copyright and license notice covers the images in this directory.
************************************************************************
TITLE: Crystal Project Icons
AUTHOR: Everaldo Coelho
SITE: http://www.everaldo.com
CONTACT: everaldo@everaldo.com
Copyright (c) 2006-2007 Everaldo Coelho.

@ -1 +0,0 @@
dom0-update-avail icon from gnome-packagekit project distributed under GPLv2

@ -1 +0,0 @@
Color padlock images downloaded from www.openclipart.org

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

@ -545,12 +545,13 @@ static void select_loop(libvchan_t *vchan)
static void usage(char *name)
{
fprintf(stderr,
"usage: %s [-w timeout] [-t] [-T] -d domain_name ["
"usage: %s [-w timeout] [-W] [-t] [-T] -d domain_name ["
"-l local_prog|"
"-c request_id,src_domain_name,src_domain_id|"
"-e] remote_cmdline\n"
"-e means exit after sending cmd,\n"
"-t enables replacing problematic bytes with '_' in command output, -T is the same for stderr\n"
"-W waits for connection end even in case of VM-VM (-c)\n"
"-c: connect to existing process (response to trigger service call)\n"
"-w timeout: override default connection timeout of 5s (set 0 for no timeout)\n",
name);
@ -649,6 +650,7 @@ int main(int argc, char **argv)
int msg_type;
int s;
int just_exec = 0;
int wait_connection_end = 0;
int connect_existing = 0;
char *local_cmdline = NULL;
char *remote_cmdline = NULL;
@ -657,7 +659,7 @@ int main(int argc, char **argv)
int src_domain_id = 0; /* if not -c given, the process is run in dom0 */
int connection_timeout = 5;
struct service_params svc_params;
while ((opt = getopt(argc, argv, "d:l:ec:tTw:")) != -1) {
while ((opt = getopt(argc, argv, "d:l:ec:tTw:W")) != -1) {
switch (opt) {
case 'd':
domname = strdup(optarg);
@ -682,6 +684,9 @@ int main(int argc, char **argv)
case 'w':
connection_timeout = atoi(optarg);
break;
case 'W':
wait_connection_end = 1;
break;
default:
usage(argv[0]);
}
@ -757,13 +762,25 @@ int main(int argc, char **argv)
strlen(remote_cmdline) + 1,
&data_domain,
&data_port);
close(s);
if (wait_connection_end && connect_existing)
/* save socket fd, 's' will be reused for the other qrexec-daemon
* connection */
wait_connection_end = s;
else
close(s);
setenv("QREXEC_REMOTE_DOMAIN", domname, 1);
prepare_local_fds(local_cmdline);
if (connect_existing) {
s = connect_unix_socket(src_domain_name);
send_service_connect(s, request_id, data_domain, data_port);
close(s);
if (wait_connection_end) {
/* wait for EOF */
fd_set read_fd;
FD_ZERO(&read_fd);
FD_SET(wait_connection_end, &read_fd);
select(wait_connection_end+1, &read_fd, NULL, NULL, 0);
}
} else {
data_vchan = libvchan_server_init(data_domain, data_port,
VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE);

@ -71,6 +71,12 @@ int policy_pending_max = -1;
* either VCHAN_PORT_* or remote domain id for used port */
int used_vchan_ports[MAX_CLIENTS];
/* notify client (close its connection) when connection initiated by it was
* terminated - used by qrexec-policy to cleanup (disposable) VM; indexed with
* vchan port number relative to VCHAN_BASE_DATA_PORT; stores fd of given
* client or -1 if none requested */
int vchan_port_notify_client[MAX_CLIENTS];
int max_client_fd = -1; // current max fd of all clients; so that we need not to scan all the "clients" table
int qrexec_daemon_unix_socket_fd; // /var/run/qubes/qrexec.xid descriptor
const char *default_user = "user";
@ -312,6 +318,7 @@ void init(int xid)
clients[i].state = CLIENT_INVALID;
policy_pending[i].pid = 0;
used_vchan_ports[i] = VCHAN_PORT_UNUSED;
vchan_port_notify_client[i] = VCHAN_PORT_UNUSED;
}
/* When running as root, make the socket accessible; perms on /var/run/qubes still apply */
@ -358,14 +365,6 @@ static int allocate_vchan_port(int new_state)
return -1;
}
static void release_vchan_port(int port, int expected_remote_id)
{
/* release only if was reserved for connection to given domain */
if (used_vchan_ports[port-VCHAN_BASE_DATA_PORT] == expected_remote_id) {
used_vchan_ports[port-VCHAN_BASE_DATA_PORT] = VCHAN_PORT_UNUSED;
}
}
static void handle_new_client()
{
int fd = do_accept(qrexec_daemon_unix_socket_fd);
@ -387,8 +386,25 @@ static void handle_new_client()
static void terminate_client(int fd)
{
int port;
clients[fd].state = CLIENT_INVALID;
close(fd);
/* if client requested vchan connection end notify, cancel it */
for (port = 0; port < MAX_CLIENTS; port++) {
if (vchan_port_notify_client[port] == fd)
vchan_port_notify_client[port] = VCHAN_PORT_UNUSED;
}
}
static void release_vchan_port(int port, int expected_remote_id)
{
/* release only if was reserved for connection to given domain */
if (used_vchan_ports[port-VCHAN_BASE_DATA_PORT] == expected_remote_id) {
used_vchan_ports[port-VCHAN_BASE_DATA_PORT] = VCHAN_PORT_UNUSED;
/* notify client if requested - it will clear notification request */
if (vchan_port_notify_client[port-VCHAN_BASE_DATA_PORT] != VCHAN_PORT_UNUSED)
terminate_client(vchan_port_notify_client[port-VCHAN_BASE_DATA_PORT]);
}
}
static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
@ -433,6 +449,8 @@ static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
terminate_client(fd);
return 0;
}
/* notify the client when this connection got terminated */
vchan_port_notify_client[params.connect_port-VCHAN_BASE_DATA_PORT] = fd;
client_params.connect_port = params.connect_port;
client_params.connect_domain = remote_domain_id;
hdr->len = sizeof(client_params);
@ -694,7 +712,7 @@ static void handle_execute_service(void)
signal(SIGPIPE, SIG_DFL);
snprintf(remote_domain_id_str, sizeof(remote_domain_id_str), "%d",
remote_domain_id);
execl("/usr/lib/qubes/qrexec-policy", "qrexec-policy", "--",
execl("/usr/bin/qrexec-policy", "qrexec-policy", "--",
remote_domain_id_str, remote_domain_name, params.target_domain,
params.service_name, params.request_id.ident, NULL);
perror("execl");

@ -1,257 +0,0 @@
#!/usr/bin/python
import sys
import os
import os.path
import subprocess
from qubes.qubes import vmm
from qubes.qubes import QubesVmCollection
import qubes.guihelpers
import libvirt
from optparse import OptionParser
import fcntl
POLICY_FILE_DIR="/etc/qubes-rpc/policy"
# XXX: Backward compatibility, to be removed soon
DEPRECATED_POLICY_FILE_DIR="/etc/qubes_rpc/policy"
QREXEC_CLIENT="/usr/lib/qubes/qrexec-client"
QUBES_RPC_MULTIPLEXER_PATH="/usr/lib/qubes/qubes-rpc-multiplexer"
class UserChoice:
ALLOW=0
DENY=1
ALWAYS_ALLOW=2
def line_to_dict(line):
tokens=line.split()
if len(tokens) < 3:
return None
if tokens[0][0] == '#':
return None
dict={}
dict['source']=tokens[0]
dict['dest']=tokens[1]
dict['full-action']=tokens[2]
action_list=tokens[2].split(',')
dict['action']=action_list.pop(0)
for iter in action_list:
paramval=iter.split("=")
dict["action."+paramval[0]]=paramval[1]
# Warn if we're ignoring extra data after a space, such as:
# vm1 vm2 allow, user=foo
if len(tokens) > 3:
print >>sys.stderr, "Trailing data ignored in %s" % line
return dict
def read_policy_file(service_name):
policy_file = os.path.join(POLICY_FILE_DIR, service_name)
if not os.path.isfile(policy_file):
# fallback to policy without specific argument set (if any)
policy_file = os.path.join(POLICY_FILE_DIR, service_name.split("+")[0])
if not os.path.isfile(policy_file):
policy_file = os.path.join(DEPRECATED_POLICY_FILE_DIR, service_name)
if not os.path.isfile(policy_file):
return None
print >>sys.stderr, "RPC service '%s' uses deprecated policy location, please move to %s" % (service_name, POLICY_FILE_DIR)
policy_list=list()
f = open(policy_file)
fcntl.flock(f, fcntl.LOCK_SH)
for iter in f.readlines():
dict = line_to_dict(iter)
if dict is not None:
policy_list.append(dict)
f.close()
return policy_list
def is_match(item, config_term):
return (item != "dom0" and config_term == "$anyvm") or item == config_term
def get_default_policy():
dict={}
dict["action"]="deny"
return dict
def find_policy(policy, domain, target):
for iter in policy:
if not is_match(domain, iter["source"]):
continue
if not is_match(target, iter["dest"]):
continue
return iter
return get_default_policy()
def validate_target(target):
# special targets
if target in ['$dispvm']:
return True
qc = QubesVmCollection()
qc.lock_db_for_reading()
qc.load()
qc.unlock_db()
return qc.get_vm_by_name(target)
def spawn_target_if_necessary(vm):
if vm.is_running():
return
# use qvm-run instead of vm.start() to make sure that nothing is written
# to stdout and nothing is read from stdin
null = open("/dev/null", "r+")
subprocess.call(["qvm-run", "-a", "--tray", "-q", vm.name, "true"],
stdin=null, stdout=null)
null.close()
def do_execute(domain, target, user, service_name, process_ident, vm=None):
if target == "$dispvm":
cmd = "/usr/lib/qubes/qfile-daemon-dvm " + service_name + " " + domain + " " +user
os.execl(QREXEC_CLIENT, "qrexec-client",
"-d", "dom0", "-c", process_ident, cmd)
else:
if isinstance(vm, qubes.qubes.QubesVm):
spawn_target_if_necessary(vm)
if target == "dom0":
cmd = QUBES_RPC_MULTIPLEXER_PATH + " " + service_name + " " + domain
else:
cmd = user + ":QUBESRPC "+ service_name + " " + domain
# stderr should be logged in source/target VM
null = open(os.devnull, 'w')
os.dup2(null.fileno(), 2)
os.execl(QREXEC_CLIENT, "qrexec-client",
"-d", target, "-c", process_ident, cmd)
def confirm_execution(domain, target, service_name):
text = "Do you allow domain \"" +domain + "\" to execute " + service_name
text+= " operation on the domain \"" + target +"\"?<br>"
text+= " \"Yes to All\" option will automatically allow this operation in the future."
return qubes.guihelpers.ask(text, yestoall=True)
def add_always_allow(domain, target, service_name, options):
policy_file=POLICY_FILE_DIR+"/"+service_name
if not os.path.isfile(policy_file):
return None
f = open(policy_file, 'r+')
fcntl.flock(f, fcntl.LOCK_EX)
lines = []
for l in f.readlines():
lines.append(l)
lines.insert(0, "%s\t%s\tallow%s\n" % (domain, target, options))
f.seek(0)
f.write("".join(lines))
f.close()
def info_dialog(msg_type, text):
if msg_type not in ['info', 'warning', 'error', 'entry']:
raise ValueError("Invalid msg_type value")
if msg_type in ['info', 'warning', 'error']:
try:
subprocess.call(["/usr/bin/zenity", "--{}".format(msg_type), "--text",
text])
except OSError:
kdialog_msg_type = {
'info': 'msgbox',
'warning': 'sorry',
'error': 'error'
}[msg_type]
subprocess.call(["/usr/bin/kdialog", "--{}".format(kdialog_msg_type), text])
else:
response = subprocess.check_output(["/usr/bin/zenity", "--{}".format(msg_type), "--text",
text])
return response
def policy_editor(domain, target, service_name):
text = "No policy definition found for " + service_name + " action. \n"
text+= "Type YES if you want to create a default policy file"
response = info_dialog('entry', text)
if response.strip() == 'YES':
create_policy(service_name)
def create_policy(service_name):
policyFile = "/etc/qubes-rpc/policy/"+service_name
policy = open(policyFile, "w")
policy.write("## Note that policy parsing stops at the first match,\n")
policy.write("## so adding anything below \"$anyvm $anyvm action\" line will have no effect\n")
policy.write("\n")
policy.write("## Please use a single # to start your custom comments\n")
policy.write("\n")
policy.write("$anyvm $anyvm ask\n")
policy.close()
def main():
usage = "usage: %prog [options] <src-domain-id> <src-domain> <target-domain> <service> <process-ident>"
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_id=args[0]
domain=args[1]
target=args[2]
service_name=args[3]
process_ident=args[4]
# Add source domain information, required by qrexec-client for establishing
# connection
process_ident+=","+domain+","+domain_id
vm = validate_target(target)
if vm is None:
print >> sys.stderr, "Rpc failed (unknown domain):", domain, target, service_name
text = "Domain '%s' doesn't exist (service %s called by domain %s)." % (
target, service_name, domain)
info_dialog("error", text)
exit(1)
policy_list=read_policy_file(service_name)
if policy_list==None:
policy_editor(domain, target, service_name)
policy_list=read_policy_file(service_name)
if policy_list==None:
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, service_name)
if user_choice == UserChoice.ALWAYS_ALLOW:
add_always_allow(domain, target, service_name, policy_dict["full-action"].lstrip('ask'))
policy_dict["action"] = "allow"
elif user_choice == UserChoice.ALLOW:
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"]
if policy_dict.has_key("action.user"):
user=policy_dict["action.user"]
else:
user="DEFAULT"
print >> sys.stderr, "Rpc allowed:", domain, target, service_name
do_execute(domain, target, user, service_name, process_ident, vm=vm)
print >> sys.stderr, "Rpc denied:", domain, target, service_name
exit(1)
main()

@ -47,6 +47,7 @@ BuildRequires: qubes-utils-devel >= 3.1.3
BuildRequires: qubes-libvchan-devel
Requires: qubes-core-dom0
Requires: qubes-utils >= 3.1.3
Requires: python3-PyQt4
Requires: %{name}-kernel-install
Requires: xdotool
@ -76,8 +77,6 @@ ln -sf . %{name}-%{version}
%setup -T -D
%build
python -m compileall appmenus-scripts
python -O -m compileall appmenus-scripts
(cd dom0-updates; make)
(cd qrexec; make)
(cd file-copy-vm; make)
@ -85,25 +84,9 @@ python -O -m compileall appmenus-scripts
%install
### Appmenus
mkdir -p $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules
cp appmenus-scripts/qubes-core-appmenus.py $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules/10appmenus.py
cp appmenus-scripts/qubes-core-appmenus.pyc $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules/10appmenus.pyc
cp appmenus-scripts/qubes-core-appmenus.pyo $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules/10appmenus.pyo
mkdir -p $RPM_BUILD_ROOT/usr/libexec/qubes-appmenus
cp appmenus-scripts/*.sh $RPM_BUILD_ROOT/usr/libexec/qubes-appmenus/
cp appmenus-scripts/qubes-receive-appmenus $RPM_BUILD_ROOT/usr/libexec/qubes-appmenus/
install -D appmenus-scripts/qvm-sync-appmenus $RPM_BUILD_ROOT/usr/bin/qvm-sync-appmenus
mkdir -p $RPM_BUILD_ROOT/etc/qubes-rpc/policy
cp appmenus-scripts/qubes.SyncAppMenus $RPM_BUILD_ROOT/etc/qubes-rpc/
cp appmenus-scripts/qubes.SyncAppMenus.policy $RPM_BUILD_ROOT/etc/qubes-rpc/policy/qubes.SyncAppMenus
mkdir -p $RPM_BUILD_ROOT/usr/share/qubes-appmenus/
cp -r appmenus-files/* $RPM_BUILD_ROOT/usr/share/qubes-appmenus/
## Appmenus
install -d $RPM_BUILD_ROOT/etc/qubes-rpc/policy
cp qubesappmenus/qubes.SyncAppMenus.policy $RPM_BUILD_ROOT/etc/qubes-rpc/policy/qubes.SyncAppMenus
### Dom0 updates
install -D dom0-updates/qubes-dom0-updates.cron $RPM_BUILD_ROOT/etc/cron.daily/qubes-dom0-updates.cron
@ -118,12 +101,12 @@ install -m 0664 -D dom0-updates/qubes.ReceiveUpdates.policy $RPM_BUILD_ROOT/etc/
install -d $RPM_BUILD_ROOT/var/lib/qubes/updates
# Qrexec
mkdir -p $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/
mkdir -p $RPM_BUILD_ROOT/usr/bin $RPM_BUILD_ROOT/usr/sbin
install qrexec/qrexec-daemon $RPM_BUILD_ROOT/usr/sbin/
install qrexec/qrexec-client $RPM_BUILD_ROOT/usr/bin/
# XXX: Backward compatibility
ln -s qrexec-client $RPM_BUILD_ROOT/usr/lib/qubes/qrexec_client
cp qrexec/qrexec-policy $RPM_BUILD_ROOT/usr/lib/qubes/
ln -s ../../bin/qrexec-client $RPM_BUILD_ROOT/usr/lib/qubes/qrexec-client
ln -s ../../sbin/qrexec-daemon $RPM_BUILD_ROOT/usr/lib/qubes/qrexec-daemon
cp qrexec/qubes-rpc-multiplexer $RPM_BUILD_ROOT/usr/lib/qubes
### pm-utils
@ -167,12 +150,6 @@ install -m 755 file-copy-vm/qfile-dom0-agent $RPM_BUILD_ROOT/usr/lib/qubes/
install -m 755 file-copy-vm/qvm-copy-to-vm $RPM_BUILD_ROOT/usr/bin/
ln -s qvm-copy-to-vm $RPM_BUILD_ROOT/usr/bin/qvm-move-to-vm
### Icons
mkdir -p $RPM_BUILD_ROOT/usr/share/qubes/icons
for icon in icons/*.png; do
convert -resize 48 $icon $RPM_BUILD_ROOT/usr/share/qubes/$icon
done
### Documentation
(cd doc; make DESTDIR=$RPM_BUILD_ROOT install)
@ -183,13 +160,6 @@ fi
%post
for i in /usr/share/qubes/icons/*.png ; do
xdg-icon-resource install --noupdate --novendor --size 48 $i
done
xdg-icon-resource forceupdate
xdg-desktop-menu install /usr/share/qubes-appmenus/qubes-dispvm.directory /usr/share/qubes-appmenus/qubes-dispvm-*.desktop
/usr/lib/qubes/patch-dnf-yum-config
systemctl enable qubes-suspend.service >/dev/null 2>&1
@ -198,12 +168,6 @@ systemctl enable qubes-suspend.service >/dev/null 2>&1
if [ "$1" = 0 ] ; then
# no more packages left
for i in /usr/share/qubes/icons/*.png ; do
xdg-icon-resource uninstall --novendor --size 48 $i
done
xdg-desktop-menu uninstall /usr/share/qubes-appmenus/qubes-dispvm.directory /usr/share/qubes-appmenus/qubes-dispvm-*.desktop
systemctl disable qubes-suspend.service > /dev/null 2>&1
fi
@ -219,29 +183,7 @@ rm -f /lib/udev/rules.d/69-xorg-vmmouse.rules
chmod -x /etc/grub.d/10_linux
%files
%attr(2775,root,qubes) %dir /etc/qubes-rpc
%attr(2775,root,qubes) %dir /etc/qubes-rpc/policy
/etc/qubes-rpc/policy/qubes.SyncAppMenus
/etc/qubes-rpc/qubes.SyncAppMenus
%{python_sitearch}/qubes/modules/10appmenus.py
%{python_sitearch}/qubes/modules/10appmenus.pyc
%{python_sitearch}/qubes/modules/10appmenus.pyo
/usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh
/usr/libexec/qubes-appmenus/convert-dirtemplate2vm.sh
/usr/libexec/qubes-appmenus/create-apps-for-appvm.sh
/usr/libexec/qubes-appmenus/qubes-receive-appmenus
/usr/libexec/qubes-appmenus/remove-appvm-appmenus.sh
/usr/share/qubes-appmenus/qubes-appmenu-select.desktop
/usr/share/qubes-appmenus/qubes-dispvm-firefox.desktop
/usr/share/qubes-appmenus/qubes-dispvm-xterm.desktop
/usr/share/qubes-appmenus/qubes-dispvm.directory
/usr/share/qubes-appmenus/qubes-servicevm.directory.template
/usr/share/qubes-appmenus/qubes-start.desktop
/usr/share/qubes-appmenus/qubes-templatevm.directory.template
/usr/share/qubes-appmenus/qubes-vm.directory.template
/usr/share/qubes-appmenus/hvm
/usr/share/qubes/icons/*.png
/usr/bin/qvm-sync-appmenus
# Dom0 updates
/etc/cron.daily/qubes-dom0-updates.cron
/etc/yum.real.repos.d/qubes-cached.repo
@ -259,11 +201,12 @@ chmod -x /etc/grub.d/10_linux
%dir %{_dracutmoddir}/90extra-modules
%{_dracutmoddir}/90extra-modules/*
# Qrexec
%attr(4750,root,qubes) /usr/lib/qubes/qrexec-daemon
/usr/lib/qubes/qrexec-client
/usr/lib/qubes/qrexec_client
/usr/sbin/qrexec-daemon
/usr/bin/qrexec-client
/usr/lib/qubes/qubes-rpc-multiplexer
/usr/lib/qubes/qrexec-policy
# compat symlinks
/usr/lib/qubes/qrexec-client
/usr/lib/qubes/qrexec-daemon
# file copy
/usr/bin/qvm-copy-to-vm
/usr/bin/qvm-move-to-vm

@ -28,6 +28,7 @@ enable xenstored.service
enable xenstored.socket
enable xenstored_ro.socket
enable xenconsoled.service
enable xen-init-dom0.service
enable libvirtd.service
enable virlockd.socket
@ -45,6 +46,7 @@ enable qubes-qmemman.service
enable qubes-suspend.service
enable qubes-setupdvm.service
enable qubes-block-cleaner.service
enable qubesd.service
enable anti-evil-maid-unseal.service
enable anti-evil-maid-check-mount-devs.service
enable anti-evil-maid-seal.service

Loading…
Cancel
Save