From 48ae89fe62c1ed18e8f26477b737a73d5db6273f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 20 Apr 2020 03:29:29 +0200 Subject: [PATCH] Make qubesd connected directly as an socket qrexec service Remove intermediate qubesd-query-fast proxy process. This requires changing socket protocol to match what qrexec is sending in the header. Fixes QubesOS/qubes-issues#3293 --- Makefile | 2 +- qubes/api/__init__.py | 16 ++++++++++++++-- qubes/tests/api.py | 25 +++++++++++++++++-------- qubes/tools/qubesd_query.py | 5 +++-- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 8816a45f..406c20f4 100644 --- a/Makefile +++ b/Makefile @@ -207,7 +207,7 @@ endif install -m 0755 qvm-tools/qvm-sync-clock $(DESTDIR)/usr/bin/qvm-sync-clock install -m 0755 qvm-tools/qvm-console-dispvm $(DESTDIR)/usr/bin/qvm-console-dispvm for method in $(ADMIN_API_METHODS_SIMPLE); do \ - ln -s ../../usr/libexec/qubes/qubesd-query-fast \ + ln -s ../../var/run/qubesd.sock \ $(DESTDIR)/etc/qubes-rpc/$$method || exit 1; \ done install qubes-rpc/admin.vm.volume.Import $(DESTDIR)/etc/qubes-rpc/ diff --git a/qubes/api/__init__.py b/qubes/api/__init__.py index 1d273d0e..43472346 100644 --- a/qubes/api/__init__.py +++ b/qubes/api/__init__.py @@ -252,8 +252,20 @@ class QubesDaemonProtocol(asyncio.Protocol): def eof_received(self): try: - src, meth, dest, arg, untrusted_payload = \ - self.untrusted_buffer.getvalue().split(b'\0', 4) + connection_params, untrusted_payload = \ + self.untrusted_buffer.getvalue().split(b'\0', 1) + meth_arg, src, dest_type, dest = \ + connection_params.split(b' ', 3) + if dest_type == b'keyword' and dest == b'adminvm': + dest_type, dest = b'name', b'dom0' + if dest_type != b'name': + raise ValueError( + 'got {} destination type, ' + 'while only explicit name supported'.format(dest_type)) + if b'+' in meth_arg: + meth, arg = meth_arg.split(b'+', 1) + else: + meth, arg = meth_arg, b'' except ValueError: self.app.log.warning('framing error') self.transport.abort() diff --git a/qubes/tests/api.py b/qubes/tests/api.py index 41b1c2d5..dfdc21a8 100644 --- a/qubes/tests/api.py +++ b/qubes/tests/api.py @@ -118,18 +118,18 @@ class TC_00_QubesDaemonProtocol(qubes.tests.QubesTestCase): super(TC_00_QubesDaemonProtocol, self).tearDown() def test_000_message_ok(self): - self.writer.write(b'dom0\0mgmt.success\0dom0\0arg\0payload') + self.writer.write(b'mgmt.success+arg src name dest\0payload') self.writer.write_eof() with self.assertNotRaises(asyncio.TimeoutError): response = self.loop.run_until_complete( asyncio.wait_for(self.reader.read(), 1)) self.assertEqual(response, - b"0\0src: b'dom0', dest: b'dom0', arg: b'arg', payload: b'payload'") + b"0\0src: b'src', dest: b'dest', arg: b'arg', payload: b'payload'") def test_001_message_ok_in_parts(self): - self.writer.write(b'dom0\0mgmt.') + self.writer.write(b'mgmt.success+arg') self.loop.run_until_complete(self.writer.drain()) - self.writer.write(b'success\0dom0\0arg\0payload') + self.writer.write(b' dom0 name dom0\0payload') self.writer.write_eof() with self.assertNotRaises(asyncio.TimeoutError): response = self.loop.run_until_complete( @@ -138,7 +138,7 @@ class TC_00_QubesDaemonProtocol(qubes.tests.QubesTestCase): b"0\0src: b'dom0', dest: b'dom0', arg: b'arg', payload: b'payload'") def test_002_message_ok_empty(self): - self.writer.write(b'dom0\0mgmt.success_none\0dom0\0arg\0payload') + self.writer.write(b'mgmt.success_none+arg dom0 name dom0\0payload') self.writer.write_eof() with self.assertNotRaises(asyncio.TimeoutError): response = self.loop.run_until_complete( @@ -146,7 +146,7 @@ class TC_00_QubesDaemonProtocol(qubes.tests.QubesTestCase): self.assertEqual(response, b"0\0") def test_003_exception_qubes(self): - self.writer.write(b'dom0\0mgmt.qubesexception\0dom0\0arg\0payload') + self.writer.write(b'mgmt.qubesexception+arg dom0 name dom0\0payload') self.writer.write_eof() with self.assertNotRaises(asyncio.TimeoutError): response = self.loop.run_until_complete( @@ -154,7 +154,7 @@ class TC_00_QubesDaemonProtocol(qubes.tests.QubesTestCase): self.assertEqual(response, b"2\0QubesException\0\0qubes-exception\0") def test_004_exception_generic(self): - self.writer.write(b'dom0\0mgmt.exception\0dom0\0arg\0payload') + self.writer.write(b'mgmt.exception+arg dom0 name dom0\0payload') self.writer.write_eof() with self.assertNotRaises(asyncio.TimeoutError): response = self.loop.run_until_complete( @@ -162,7 +162,7 @@ class TC_00_QubesDaemonProtocol(qubes.tests.QubesTestCase): self.assertEqual(response, b"") def test_005_event(self): - self.writer.write(b'dom0\0mgmt.event\0dom0\0arg\0payload') + self.writer.write(b'mgmt.event+arg dom0 name dom0\0payload') self.writer.write_eof() with self.assertNotRaises(asyncio.TimeoutError): response = self.loop.run_until_complete( @@ -174,3 +174,12 @@ class TC_00_QubesDaemonProtocol(qubes.tests.QubesTestCase): with self.assertNotRaises(asyncio.TimeoutError): self.loop.run_until_complete( asyncio.wait_for(self.protocol.mgmt.task, 1)) + + def test_006_target_adminvm(self): + self.writer.write(b'mgmt.success+arg src keyword adminvm\0payload') + self.writer.write_eof() + with self.assertNotRaises(asyncio.TimeoutError): + response = self.loop.run_until_complete( + asyncio.wait_for(self.reader.read(), 1)) + self.assertEqual(response, + b"0\0src: b'src', dest: b'dom0', arg: b'arg', payload: b'payload'") diff --git a/qubes/tools/qubesd_query.py b/qubes/tools/qubesd_query.py index 5447d727..49de1d0a 100644 --- a/qubes/tools/qubesd_query.py +++ b/qubes/tools/qubesd_query.py @@ -83,8 +83,9 @@ def main(args=None): payload = sys.stdin.buffer.read() if args.payload else b'' # pylint: enable=no-member - coro = asyncio.ensure_future(qubesd_client(args.socket, payload, - args.src, args.method, args.dest, args.arg)) + coro = asyncio.ensure_future(qubesd_client( + args.socket, payload, + f'{args.method}+{args.arg} {args.src} name {args.dest}')) for signame in ('SIGINT', 'SIGTERM'): loop.add_signal_handler(getattr(signal, signame),