Browse Source

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
Marek Marczykowski-Górecki 4 years ago
parent
commit
48ae89fe62
4 changed files with 35 additions and 13 deletions
  1. 1 1
      Makefile
  2. 14 2
      qubes/api/__init__.py
  3. 17 8
      qubes/tests/api.py
  4. 3 2
      qubes/tools/qubesd_query.py

+ 1 - 1
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/

+ 14 - 2
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()

+ 17 - 8
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'")

+ 3 - 2
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),