Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions daemon/qrexec-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ static struct option longopts[] = {
{ "help", no_argument, 0, 'h' },
{ "socket-dir", required_argument, 0, opt_socket_dir },
{ "no-exit-code", no_argument, 0, 'E' },
{ "prefix-data", required_argument, 0, 'p' },
{ "use-stdin-socket", no_argument, 0, opt_use_stdin_socket },
{ NULL, 0, 0, 0 },
};
Expand All @@ -108,6 +109,7 @@ _Noreturn static void usage(const char *const name, int status)
" -w timeout - override default connection timeout of 5s (set 0 for no timeout)\n"
" -k - kill the domain right before exiting\n"
" --socket-dir=PATH - directory for qrexec socket, default: %s\n"
" -p PREFIX-DATA, --prefix-data=PREFIX-DATA - send the given data before the provided stdin (can only be used once)\n"
" --use-stdin-socket - use fd 0 (which must be socket) for both stdin and stdout\n",
name ? name : "qrexec-client", QREXEC_DAEMON_SOCKET_DIR);
exit(status);
Expand Down Expand Up @@ -177,11 +179,12 @@ int main(int argc, char **argv)
bool replace_chars_stderr = false;
bool wait_connection_end = false;
bool exit_with_code = true;
const char *prefix_data = NULL;
int rc = QREXEC_EXIT_PROBLEM;

setup_logging("qrexec-client");

while ((opt = getopt_long(argc, argv, "hd:l:eEc:tTw:Wk", longopts, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "hd:l:eEc:p:tTw:Wk", longopts, NULL)) != -1) {
switch (opt) {
case 'd':
domname = xstrdup(optarg);
Expand All @@ -206,6 +209,11 @@ int main(int argc, char **argv)
usage(argv[0], 1);
}
break;
case 'p':
if (prefix_data)
usage(argv[0], 2);
prefix_data = optarg;
break;
case 't':
replace_chars_stdout = true;
break;
Expand Down Expand Up @@ -281,7 +289,8 @@ int main(int argc, char **argv)
src_domain_name,
remote_cmdline,
connection_timeout,
exit_with_code);
exit_with_code,
prefix_data);
} else {
/* dom0 -> dom0 fake service call */
assert(src_domain_id == 0);
Expand Down Expand Up @@ -398,6 +407,7 @@ int main(int argc, char **argv)
.exit_with_code = exit_with_code,
.replace_chars_stdout = replace_chars_stdout,
.replace_chars_stderr = replace_chars_stderr,
.prefix_data = prefix_data,
};
rc = handshake_and_go(&params, NULL);
cleanup:
Expand Down
13 changes: 10 additions & 3 deletions daemon/qrexec-daemon-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,8 +478,13 @@ static int select_loop(const struct handshake_params *params,
req.data_protocol_version = params->data_protocol_version;
req.sigchld = &sigchld;
req.sigusr1 = NULL;
req.prefix_data.data = NULL;
req.prefix_data.len = 0;
if (params->prefix_data) {
req.prefix_data.data = params->prefix_data;
req.prefix_data.len = strlen(params->prefix_data);
} else {
req.prefix_data.data = NULL;
req.prefix_data.len = 0;
}

exit_code = qrexec_process_io(&req, cmd);
return (params->exit_with_code ? exit_code : 0);
Expand All @@ -490,7 +495,8 @@ int run_qrexec_to_dom0(const struct service_params *svc_params,
const char *src_domain_name,
char *remote_cmdline,
int connection_timeout,
bool exit_with_code)
bool exit_with_code,
const char *prefix_data)
{
int data_domain;
int data_port;
Expand Down Expand Up @@ -544,6 +550,7 @@ int run_qrexec_to_dom0(const struct service_params *svc_params,
.exit_with_code = exit_with_code,
.replace_chars_stdout = false, // stdout is _from_ dom0
.replace_chars_stderr = false, // stderr is _from_ dom0
.prefix_data = prefix_data,
};
return handshake_and_go(&params, command);
}
Expand Down
5 changes: 4 additions & 1 deletion daemon/qrexec-daemon-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ int run_qrexec_to_dom0(const struct service_params *svc_params,
const char *src_domain_name,
char *remote_cmdline,
int connection_timeout,
bool exit_with_code);
bool exit_with_code,
const char *prefix_data);
/** Parameters for handshake_and_go(), organized as a struct
* for convenience. */
struct handshake_params {
Expand All @@ -97,6 +98,8 @@ struct handshake_params {
bool replace_chars_stdout;
/// Whether to replace problematic bytes with _ before writing to stderr.
bool replace_chars_stderr;
/// Data to send after connecting
const char *prefix_data;
};
/**
* Process IO call with the parameters specified by the parameters.
Expand Down
3 changes: 2 additions & 1 deletion daemon/qrexec-daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -1256,7 +1256,8 @@ _Noreturn static void handle_execute_service_child(
source_domain,
cmd,
5 /* 5 second timeout */,
false /* return 0 not remote status code */));
false /* return 0 not remote status code */,
NULL /* prefix data */));
} else {
bool const use_uuid = target_uuid != NULL;
const char *const selected_target = use_uuid ? target_uuid : target;
Expand Down
23 changes: 20 additions & 3 deletions qrexec/tests/socket/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,9 @@ def test_run_vm_command_from_dom0(self):
target_daemon = self.connect_daemon(
target_domain, target_domain_name, target_domain_uuid
)
self.start_client(["-d", target_domain_name, cmd])
self.start_client(
["-d", target_domain_name, "-p", "prefix data\n", cmd]
)
target_daemon.accept()
target_daemon.handshake(QREXEC_PROTOCOL_V3)

Expand All @@ -754,6 +756,9 @@ def test_run_vm_command_from_dom0(self):
target.handshake()

# select_loop
self.assertEqual(
target.recv_message(), (qrexec.MSG_DATA_STDIN, b"prefix data\n")
)
target.send_message(qrexec.MSG_DATA_STDOUT, b"stdout data\n")
target.send_message(qrexec.MSG_DATA_STDOUT, b"")
self.assertEqual(self.client.stdout.read(), b"stdout data\n")
Expand Down Expand Up @@ -822,7 +827,7 @@ def test_run_vm_command_from_dom0_reject_stdin(self):

def test_run_vm_command_from_dom0_with_local_command(self):
cmd = "user:command"
local_cmd = "while read x; do echo input: $x; done; exit 44"
local_cmd = 'while read x; do echo input: "$x"; done; exit 44'
target_domain_name = "target_domain"
target_domain_uuid = "d95e1147-2d82-4595-90bb-5a7500cc3196"
target_domain = 42
Expand All @@ -832,7 +837,15 @@ def test_run_vm_command_from_dom0_with_local_command(self):
target_domain, target_domain_name, target_domain_uuid
)
self.start_client(
["-d", "uuid:" + target_domain_uuid, "-l", local_cmd, cmd]
[
"-d",
"uuid:" + target_domain_uuid,
"-l",
local_cmd,
"-p",
"prefix data\n",
cmd,
]
)
target_daemon.accept()
target_daemon.handshake(QREXEC_PROTOCOL_V3)
Expand All @@ -854,6 +867,10 @@ def test_run_vm_command_from_dom0_with_local_command(self):
target.handshake()

# select_loop
self.assertEqual(
target.recv_message(),
(qrexec.MSG_DATA_STDIN, b"prefix data\n"),
)
target.send_message(qrexec.MSG_DATA_STDOUT, b"stdout data\n")
self.assertEqual(
target.recv_message(),
Expand Down