From 9883543dba64c3d6fe10d394f307f72019a2b81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marczewski?= Date: Sat, 28 Mar 2020 20:17:18 +0100 Subject: [PATCH] qrexec-client: handle failed service exec Otherwise, qrexec-client just hangs when it fails to spawn a process, or connect to a socket service. This corresponds to the fix for qrexec-agent in commit 95fa4bb2d29fcfc91da3ada95300258450022610. --- daemon/qrexec-client.c | 32 +++++++++++++++++++++++++++----- qrexec/tests/socket/daemon.py | 12 ++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/daemon/qrexec-client.c b/daemon/qrexec-client.c index c01b104e..8caa53f0 100644 --- a/daemon/qrexec-client.c +++ b/daemon/qrexec-client.c @@ -252,20 +252,37 @@ static void wait_for_session_maybe(char *cmdline) destroy_qrexec_parsed_command(cmd); } -static void prepare_local_fds(char *cmdline, struct buffer *stdin_buffer) +static int prepare_local_fds(char *cmdline, struct buffer *stdin_buffer) { if (stdin_buffer == NULL) abort(); if (!cmdline) { local_stdin_fd = 1; local_stdout_fd = 0; - return; + return 0; } signal(SIGCHLD, sigchld_handler); - execute_qubes_rpc_command(cmdline, &local_pid, &local_stdin_fd, &local_stdout_fd, + return execute_qubes_rpc_command(cmdline, &local_pid, &local_stdin_fd, &local_stdout_fd, NULL, false, stdin_buffer); } +// See also qrexec-agent/qrexec-agent-data.c +static _Noreturn void handle_failed_exec(libvchan_t *data_vchan) +{ + int exit_code = 127; + struct msg_header hdr = { + .type = MSG_DATA_STDOUT, + .len = 0, + }; + + LOG(ERROR, "failed to spawn process"); + libvchan_send(data_vchan, &hdr, sizeof(hdr)); + send_exit_code(data_vchan, exit_code); + libvchan_close(data_vchan); + + exit(exit_code); +} + /* ask the daemon to allocate vchan port */ static void negotiate_connection_params(int s, int other_domid, unsigned type, void *cmdline_param, int cmdline_size, @@ -491,6 +508,7 @@ int main(int argc, char **argv) int connection_timeout = 5; struct service_params svc_params; int data_protocol_version; + int prepare_ret; setup_logging("qrexec-client"); @@ -572,7 +590,7 @@ int main(int argc, char **argv) struct buffer stdin_buffer; buffer_init(&stdin_buffer); wait_for_session_maybe(remote_cmdline); - prepare_local_fds(remote_cmdline, &stdin_buffer); + prepare_ret = prepare_local_fds(remote_cmdline, &stdin_buffer); if (connect_existing) { void (*old_handler)(int); @@ -595,6 +613,8 @@ int main(int argc, char **argv) data_protocol_version = handle_agent_handshake(data_vchan, connect_existing); if (data_protocol_version < 0) exit(1); + if (prepare_ret < 0) + handle_failed_exec(data_vchan); select_loop(data_vchan, data_protocol_version, &stdin_buffer); } else { msg_type = just_exec ? MSG_JUST_EXEC : MSG_EXEC_CMDLINE; @@ -615,7 +635,7 @@ int main(int argc, char **argv) set_remote_domain(domname); struct buffer stdin_buffer; buffer_init(&stdin_buffer); - prepare_local_fds(local_cmdline, &stdin_buffer); + prepare_ret = prepare_local_fds(local_cmdline, &stdin_buffer); if (connect_existing) { s = connect_unix_socket(src_domain_name); send_service_connect(s, request_id, data_domain, data_port); @@ -642,6 +662,8 @@ int main(int argc, char **argv) data_protocol_version = handle_agent_handshake(data_vchan, 0); if (data_protocol_version < 0) exit(1); + if (prepare_ret < 0) + handle_failed_exec(data_vchan); select_loop(data_vchan, data_protocol_version, &stdin_buffer); } } diff --git a/qrexec/tests/socket/daemon.py b/qrexec/tests/socket/daemon.py index 81122b67..dea81ea4 100644 --- a/qrexec/tests/socket/daemon.py +++ b/qrexec/tests/socket/daemon.py @@ -519,6 +519,18 @@ def test_run_dom0_service_exec(self): self.client.wait() self.assertEqual(self.client.returncode, 0) + def test_run_dom0_service_failed(self): + # qubes.Service does not exist + cmd = 'QUBESRPC qubes.Service+arg src_domain name src_domain' + source = self.connect_service_request(cmd) + + self.assertEqual(source.recv_all_messages(), [ + (qrexec.MSG_DATA_STDOUT, b''), + (qrexec.MSG_DATA_EXIT_CODE, struct.pack('