core-admin/qubes/api/internal.py
Marek Marczykowski-Górecki 3cacf290bb
admin: implement admin.vm.volume.Import
Implement this in two parts:
1. Permissions checks, getting a path from appropriate storage pool
2. Actual data import

The first part is done by qubesd in a standard way, but then, instead of
accepting all the data (which may be several GB), return a path to which
a shell script (in practice: `dd` command) will write the data.
Then the script call back to qubesd again to report success/failure and
qubesd response from that call is actually returned to the user.

This way we do not pass all the data through qubesd, but still can
control the process from there in a meaningful way. Note that the last
part (second call to qubesd) may perform all kind of verification (like
a signature check on the data, or so) and can also prevent VM from
starting (hooking also domain-pre-start event) from not verified image.

QubesOS/qubes-issues#2622
2017-05-26 15:08:14 +02:00

110 lines
3.5 KiB
Python

# -*- encoding: utf8 -*-
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2017 Marek Marczykowski-Górecki
# <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, see <http://www.gnu.org/licenses/>.
''' Internal interface for dom0 components to communicate with qubesd. '''
import asyncio
import json
import qubes.api
import qubes.api.admin
import qubes.vm.dispvm
class QubesInternalAPI(qubes.api.AbstractQubesAPI):
''' Communication interface for dom0 components,
by design the input here is trusted.'''
#
# PRIVATE METHODS, not to be called via RPC
#
#
# ACTUAL RPC CALLS
#
@qubes.api.method('internal.GetSystemInfo', no_payload=True)
@asyncio.coroutine
def getsysteminfo(self):
assert self.dest.name == 'dom0'
assert not self.arg
system_info = {'domains': {
domain.name: {
'tags': list(domain.tags),
'type': domain.__class__.__name__,
'dispvm_allowed': getattr(domain, 'dispvm_allowed', False),
'default_dispvm': (str(domain.default_dispvm) if
domain.default_dispvm else None),
'icon': str(domain.label.icon),
} for domain in self.app.domains
}}
return json.dumps(system_info)
@qubes.api.method('internal.vm.Start', no_payload=True)
@asyncio.coroutine
def start(self):
assert not self.arg
if self.dest.name == 'dom0':
return
yield from self.dest.start()
@qubes.api.method('internal.vm.Create.DispVM', no_payload=True)
@asyncio.coroutine
def create_dispvm(self):
assert not self.arg
# TODO convert to coroutine
dispvm = qubes.vm.dispvm.DispVM.from_appvm(self.dest)
return dispvm.name
@qubes.api.method('internal.vm.CleanupDispVM', no_payload=True)
@asyncio.coroutine
def cleanup_dispvm(self):
assert not self.arg
# TODO convert to coroutine
self.dest.cleanup()
@qubes.api.method('internal.vm.volume.ImportEnd')
@asyncio.coroutine
def vm_volume_import_end(self, untrusted_payload):
'''
This is second half of admin.vm.volume.Import handling. It is called
when actual import is finished. Response from this method is sent do
the client (as a response for admin.vm.volume.Import call).
'''
assert self.arg in self.dest.volumes.keys()
success = untrusted_payload == b'ok'
try:
self.dest.storage.import_data_end(self.arg, success=success)
except:
self.dest.fire_event('domain-volume-import-end', volume=self.arg,
succeess=False)
raise
self.dest.fire_event('domain-volume-import-end', volume=self.arg,
succeess=success)
if not success:
raise qubes.exc.QubesException('Data import failed')