Skip to content

Commit

Permalink
Prefix data support
Browse files Browse the repository at this point in the history
This allows passing a prefix data argument to qrexec-client-vm.  Prefix
data is sent before the data on stdin, which is useful for e.g.
requesting armored or binary OpenPGP signatures.
  • Loading branch information
DemiMarie committed Dec 17, 2022
1 parent b825533 commit 6caecaf
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 24 deletions.
14 changes: 13 additions & 1 deletion agent/qrexec-agent-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ static int handle_new_process_common(
req.sigchld = &sigchld;
req.sigusr1 = &sigusr1;

req.prefix_data.data = NULL;
req.prefix_data.len = 0;

exit_code = process_io(&req);

if (type == MSG_EXEC_CMDLINE)
Expand Down Expand Up @@ -303,7 +306,8 @@ pid_t handle_new_process(int type, int connect_domain, int connect_port,
/* Returns exit code of remote process */
int handle_data_client(
int type, int connect_domain, int connect_port,
int stdin_fd, int stdout_fd, int stderr_fd, int buffer_size, pid_t pid)
int stdin_fd, int stdout_fd, int stderr_fd, int buffer_size, pid_t pid,
const char *extra_data)
{
int exit_code;
int data_protocol_version;
Expand Down Expand Up @@ -346,6 +350,14 @@ int handle_data_client(
req.sigchld = &sigchld;
req.sigusr1 = &sigusr1;

if (extra_data) {
req.prefix_data.data = extra_data;
req.prefix_data.len = strlen(extra_data);
} else {
req.prefix_data.data = NULL;
req.prefix_data.len = 0;
}

exit_code = process_io(&req);
libvchan_close(data_vchan);
return exit_code;
Expand Down
2 changes: 1 addition & 1 deletion agent/qrexec-agent.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pid_t handle_new_process(int type,
int handle_data_client(int type,
int connect_domain, int connect_port,
int stdin_fd, int stdout_fd, int stderr_fd,
int buffer_size, pid_t pid);
int buffer_size, pid_t pid, const char *extra_data);


struct qrexec_cmd_info {
Expand Down
15 changes: 11 additions & 4 deletions agent/qrexec-client-vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ static struct option longopts[] = {
{ "no-filter-escape-chars-stdout", no_argument, 0, opt_no_filter_stdout},
{ "no-filter-escape-chars-stderr", no_argument, 0, opt_no_filter_stderr},
{ "agent-socket", required_argument, 0, 'a'},
{ "prefix-data", required_argument, 0, 'p' },
{ "help", no_argument, 0, 'h' },
{ NULL, 0, 0, 0},
};
Expand All @@ -120,6 +121,7 @@ _Noreturn static void usage(const char *argv0, int status) {
fprintf(stderr, " --agent-socket=PATH - path to connect to, default: %s\n",
QREXEC_AGENT_TRIGGER_PATH);
fprintf(stderr, " -h, --help - print this message\n");
fprintf(stderr, " -p PREFIX-DATA, --prefix-data=PREFIX-DATA - send the given data before the provided stdin (can only be used once)\n");
exit(status);
}

Expand All @@ -139,15 +141,15 @@ int main(int argc, char **argv)
int inpipe[2], outpipe[2];
int buffer_size = 0;
int opt;
const char *agent_trigger_path = QREXEC_AGENT_TRIGGER_PATH;
const char *agent_trigger_path = QREXEC_AGENT_TRIGGER_PATH, *prefix_data = NULL;

setup_logging("qrexec-client-vm");

// TODO: this should be in process_io
signal(SIGPIPE, SIG_IGN);

while (1) {
opt = getopt_long(argc, argv, "+tTa:h", longopts, NULL);
opt = getopt_long(argc, argv, "+tTa:hp:", longopts, NULL);
if (opt == -1)
break;
switch (opt) {
Expand Down Expand Up @@ -179,6 +181,11 @@ int main(int argc, char **argv)
break;
case 'h':
usage(argv[0], 0);
case 'p':
if (prefix_data)
usage(argv[0], 2);
prefix_data = optarg;
break;
case opt_no_filter_stdout:
replace_chars_stdout = 0;
break;
Expand Down Expand Up @@ -293,11 +300,11 @@ int main(int argc, char **argv)

ret = handle_data_client(MSG_SERVICE_CONNECT,
exec_params.connect_domain, exec_params.connect_port,
inpipe[1], outpipe[0], -1, buffer_size, child_pid);
inpipe[1], outpipe[0], -1, buffer_size, child_pid, prefix_data);
} else {
ret = handle_data_client(MSG_SERVICE_CONNECT,
exec_params.connect_domain, exec_params.connect_port,
1, 0, -1, buffer_size, 0);
1, 0, -1, buffer_size, 0, prefix_data);
}

close(trigger_fd);
Expand Down
2 changes: 2 additions & 0 deletions daemon/qrexec-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ static void select_loop(libvchan_t *vchan, int data_protocol_version, struct buf
req.data_protocol_version = data_protocol_version;
req.sigchld = &sigchld;
req.sigusr1 = NULL;
req.prefix_data.data = NULL;
req.prefix_data.len = 0;

exit_code = process_io(&req);
libvchan_close(vchan);
Expand Down
8 changes: 7 additions & 1 deletion libqrexec/libqrexec-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ int handle_remote_data(
struct buffer *stdin_buf, int data_protocol_version,
bool replace_chars_stdout, bool replace_chars_stderr, bool is_service);

struct prefix_data {
const char *data;
size_t len;
};

/*
* Handle data from the specified FD (cannot be -1) and send it over vchan
* with a given message type (MSG_DATA_STDIN/STDOUT/STDERR).
Expand All @@ -205,7 +210,7 @@ int handle_remote_data(
*/
int handle_input(
libvchan_t *vchan, int fd, int msg_type,
int data_protocol_version);
int data_protocol_version, struct prefix_data *data);

int send_exit_code(libvchan_t *vchan, int status);

Expand Down Expand Up @@ -241,6 +246,7 @@ struct process_io_request {
volatile sig_atomic_t *sigchld;
// can be NULL
volatile sig_atomic_t *sigusr1;
struct prefix_data prefix_data;
};

/*
Expand Down
5 changes: 3 additions & 2 deletions libqrexec/process_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ int process_io(const struct process_io_request *req) {
sigset_t pollmask;
struct timespec zero_timeout = { 0, 0 };
struct timespec normal_timeout = { 10, 0 };
struct prefix_data empty = { 0, 0 }, prefix = req->prefix_data;

sigemptyset(&pollmask);
sigaddset(&pollmask, SIGCHLD);
Expand Down Expand Up @@ -297,7 +298,7 @@ int process_io(const struct process_io_request *req) {
if (stdout_fd >= 0 && fds[FD_STDOUT].revents) {
switch (handle_input(
vchan, stdout_fd, stdout_msg_type,
data_protocol_version)) {
data_protocol_version, &prefix)) {
case REMOTE_ERROR:
handle_vchan_error("send(handle_input stdout)");
break;
Expand All @@ -310,7 +311,7 @@ int process_io(const struct process_io_request *req) {
if (stderr_fd >= 0 && fds[FD_STDERR].revents) {
switch (handle_input(
vchan, stderr_fd, MSG_DATA_STDERR,
data_protocol_version)) {
data_protocol_version, &empty)) {
case REMOTE_ERROR:
handle_vchan_error("send(handle_input stderr)");
break;
Expand Down
38 changes: 23 additions & 15 deletions libqrexec/remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,13 @@ int handle_remote_data(

int handle_input(
libvchan_t *vchan, int fd, int msg_type,
int data_protocol_version)
int data_protocol_version, struct prefix_data *prefix_data)
{
const size_t max_len = max_data_chunk_size(data_protocol_version);
char *buf;
ssize_t len;
struct msg_header hdr;
int rc = REMOTE_ERROR;
int rc = REMOTE_ERROR, buf_space;

buf = malloc(max_len);
if (!buf) {
Expand All @@ -165,21 +165,29 @@ int handle_input(

static_assert(SSIZE_MAX >= INT_MAX, "can't happen on Linux");
hdr.type = msg_type;
while (libvchan_buffer_space(vchan) > (int)sizeof(struct msg_header)) {
len = libvchan_buffer_space(vchan)-sizeof(struct msg_header);
while ((buf_space = libvchan_buffer_space(vchan)) > (int)sizeof(struct msg_header)) {
len = buf_space - sizeof(struct msg_header);
if ((size_t)len > max_len)
len = max_len;
len = read(fd, buf, len);
/* If the other side of the socket is a process that is already dead,
* read from such socket could fail with ECONNRESET instead of
* just 0. */
if (len < 0 && errno == ECONNRESET)
len = 0;
if (len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
rc = REMOTE_OK;
/* otherwise keep rc = REMOTE_ERROR */
goto out;
if (prefix_data->len) {
if ((size_t)len > prefix_data->len)
len = prefix_data->len;
memcpy(buf, prefix_data->data, len);
prefix_data->data += len;
prefix_data->len -= len;
} else {
len = read(fd, buf, len);
/* If the other side of the socket is a process that is already dead,
* read from such socket could fail with ECONNRESET instead of
* just 0. */
if (len < 0 && errno == ECONNRESET)
len = 0;
if (len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
rc = REMOTE_OK;
/* otherwise keep rc = REMOTE_ERROR */
goto out;
}
}
hdr.len = (uint32_t)len;
/* do not fail on sending EOF (think: close()), it will be handled just below */
Expand Down

0 comments on commit 6caecaf

Please sign in to comment.