Skip to content

Commit 2e407b4

Browse files
committed
Merge remote-tracking branch 'origin/pr/138'
* origin/pr/138: Support not passing metadata to socket-based services Cleanly terminate connections if command or config is invalid Test service configuration better Clean up configuration loading Move TOML parsing function to private header tests: Allow running tests under ASAN+UBSAN tests: Allow altering arguments to test script tests: treat ECONNRESET as EOF tests: don't use sleep(1) to enforce message ordering tests: prevent unexpected message combining tests: tolerate alternate orders of messages
2 parents b318516 + c6ec6ef commit 2e407b4

13 files changed

+345
-118
lines changed

agent/qrexec-agent-data.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,12 @@ static int handle_just_exec(struct qrexec_parsed_command *cmd)
134134
{
135135
int fdn, pid;
136136

137+
if (cmd == NULL)
138+
return 127;
137139
switch (pid = fork()) {
138140
case -1:
139141
PERROR("fork");
140-
return -1;
142+
return 127;
141143
case 0:
142144
fdn = open("/dev/null", O_RDWR);
143145
fix_fds(fdn, fdn, fdn);
@@ -271,7 +273,8 @@ static int handle_new_process_common(
271273
return 0;
272274
case MSG_EXEC_CMDLINE:
273275
buffer_init(&stdin_buf);
274-
if (execute_parsed_qubes_rpc_command(cmd, &pid, &stdin_fd, &stdout_fd, &stderr_fd, &stdin_buf) < 0) {
276+
if (cmd == NULL ||
277+
execute_parsed_qubes_rpc_command(cmd, &pid, &stdin_fd, &stdout_fd, &stderr_fd, &stdin_buf) < 0) {
275278
struct msg_header hdr = {
276279
.type = MSG_DATA_STDOUT,
277280
.len = 0,

agent/qrexec-agent.c

+29-24
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ static const char *fork_server_path = QREXEC_FORK_SERVER_SOCKET;
8585
static void handle_server_exec_request_do(int type, int connect_domain, int connect_port,
8686
struct qrexec_parsed_command *cmd,
8787
char *cmdline);
88+
static void terminate_connection(uint32_t domain, uint32_t port);
8889

8990
const bool qrexec_is_fork_server = false;
9091

@@ -587,26 +588,20 @@ static void handle_server_exec_request_init(struct msg_header *hdr)
587588
cmd = parse_qubes_rpc_command(buf, true);
588589
if (cmd == NULL) {
589590
LOG(ERROR, "Could not parse command line: %s", buf);
590-
return;
591+
goto doit;
591592
}
592593

593594
/* load service config only for service requests */
594595
if (cmd->service_descriptor) {
595-
int wait_for_session = 0;
596-
char *user = NULL;
597-
598-
if (load_service_config(cmd, &wait_for_session, &user) < 0) {
596+
if (load_service_config_v2(cmd) < 0) {
599597
LOG(ERROR, "Could not load config for command %s", buf);
600-
return;
601-
}
602-
603-
if (user != NULL) {
604-
free(cmd->username);
605-
cmd->username = user;
598+
destroy_qrexec_parsed_command(cmd);
599+
cmd = NULL;
600+
goto doit;
606601
}
607602

608603
/* "nogui:" prefix has priority */
609-
if (!cmd->nogui && wait_for_session && wait_for_session_maybe(cmd)) {
604+
if (!cmd->nogui && cmd->wait_for_session && wait_for_session_maybe(cmd)) {
610605
/* waiting for session, postpone actual call */
611606
int slot_index;
612607
for (slot_index = 0; slot_index < MAX_FDS; slot_index++)
@@ -628,6 +623,7 @@ static void handle_server_exec_request_init(struct msg_header *hdr)
628623
}
629624
}
630625

626+
doit:
631627
handle_server_exec_request_do(hdr->type, params.connect_domain, params.connect_port, cmd, buf);
632628
destroy_qrexec_parsed_command(cmd);
633629
free(buf);
@@ -671,7 +667,7 @@ static void handle_server_exec_request_do(int type,
671667
return;
672668
}
673669

674-
if (!cmd->nogui) {
670+
if (cmd != NULL && !cmd->nogui) {
675671
/* try fork server */
676672
int child_socket = try_fork_server(type,
677673
params.connect_domain, params.connect_port,
@@ -765,20 +761,29 @@ static int find_connection(int pid)
765761
}
766762

767763
static void release_connection(int id) {
768-
struct msg_header hdr;
769-
struct exec_params params;
770-
771-
hdr.type = MSG_CONNECTION_TERMINATED;
772-
hdr.len = sizeof(struct exec_params);
773-
params.connect_domain = connection_info[id].connect_domain;
774-
params.connect_port = connection_info[id].connect_port;
775-
if (libvchan_send(ctrl_vchan, &hdr, sizeof(hdr)) != sizeof(hdr))
776-
handle_vchan_error("send (MSG_CONNECTION_TERMINATED hdr)");
777-
if (libvchan_send(ctrl_vchan, &params, sizeof(params)) != sizeof(params))
778-
handle_vchan_error("send (MSG_CONNECTION_TERMINATED data)");
764+
terminate_connection(connection_info[id].connect_domain,
765+
connection_info[id].connect_port);
779766
connection_info[id].pid = 0;
780767
}
781768

769+
static void terminate_connection(uint32_t domain, uint32_t port) {
770+
struct {
771+
struct msg_header hdr;
772+
struct exec_params params;
773+
} data = {
774+
.hdr = {
775+
.type = MSG_CONNECTION_TERMINATED,
776+
.len = sizeof(struct exec_params),
777+
},
778+
.params = {
779+
.connect_domain = domain,
780+
.connect_port = port,
781+
},
782+
};
783+
if (libvchan_send(ctrl_vchan, &data, sizeof(data)) != sizeof(data))
784+
handle_vchan_error("send (MSG_CONNECTION_TERMINATED)");
785+
}
786+
782787
static void reap_children(void)
783788
{
784789
int status;

daemon/qrexec-client.c

+2-4
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,6 @@ static _Noreturn void do_exec(const char *prog, const char *username __attribute
213213
/* See also qrexec-agent.c:wait_for_session_maybe() */
214214
static void wait_for_session_maybe(char *cmdline)
215215
{
216-
int wait_for_session = 0;
217216
struct qrexec_parsed_command *cmd;
218217
pid_t pid;
219218
int status;
@@ -228,9 +227,8 @@ static void wait_for_session_maybe(char *cmdline)
228227
if (!cmd->service_descriptor)
229228
goto out;
230229

231-
char *user = NULL;
232-
load_service_config(cmd, &wait_for_session, &user);
233-
if (!wait_for_session)
230+
load_service_config_v2(cmd);
231+
if (!cmd->wait_for_session)
234232
goto out;
235233

236234
pid = fork();

libqrexec/Makefile

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
CC=gcc
1+
CC ?=gcc
22
VCHAN_PKG = $(if $(BACKEND_VMM),vchan-$(BACKEND_VMM),vchan)
33
override QUBES_CFLAGS := -I. -I../libqrexec -g -O2 -Wall -Wextra -Werror \
44
$(shell pkg-config --cflags $(VCHAN_PKG)) -fstack-protector \
55
-D_FORTIFY_SOURCE=2 -fstack-protector-strong -fPIC -std=gnu11 -D_POSIX_C_SOURCE=200809L \
66
-D_GNU_SOURCE $(CFLAGS) \
7-
-Wstrict-prototypes -Wold-style-definition -Wmissing-declarations
7+
-Wstrict-prototypes -Wold-style-definition -Wmissing-declarations \
8+
-fno-delete-null-pointer-checks
89

910
override LDFLAGS += -pie -Wl,-z,relro,-z,now -shared
1011

libqrexec/buffer.c

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <stdio.h>
2424
#include <stdlib.h>
2525
#include <string.h>
26+
#include <assert.h>
2627
#include "libqrexec-utils.h"
2728

2829
#define BUFFER_LIMIT 50000000
@@ -79,6 +80,7 @@ void buffer_append(struct buffer *b, const char *data, int len)
7980
{
8081
int newsize;
8182
char *qdata;
83+
assert(data != NULL && "NULL data");
8284
if (b->buflen < 0 || b->buflen > BUFFER_LIMIT) {
8385
LOG(ERROR, "buffer_append buflen %d", len);
8486
exit(1);
@@ -91,7 +93,9 @@ void buffer_append(struct buffer *b, const char *data, int len)
9193
return;
9294
newsize = len + b->buflen;
9395
qdata = limited_malloc(len + b->buflen);
94-
memcpy(qdata, b->data, (size_t)b->buflen);
96+
if (b->data != 0) {
97+
memcpy(qdata, b->data, (size_t)b->buflen);
98+
}
9599
memcpy(qdata + b->buflen, data, (size_t)len);
96100
buffer_free(b);
97101
b->buflen = newsize;

libqrexec/exec.c

+32-8
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <fcntl.h>
3737
#include "qrexec.h"
3838
#include "libqrexec-utils.h"
39+
#include "private.h"
3940

4041
static do_exec_t *exec_func = NULL;
4142
void register_exec_func(do_exec_t *func) {
@@ -251,11 +252,10 @@ static int find_file(
251252
}
252253
return -1;
253254
}
254-
int load_service_config(const struct qrexec_parsed_command *cmd,
255-
int *wait_for_session, char **user) {
256-
assert(cmd->service_descriptor);
257-
assert(*user == NULL);
258255

256+
static int load_service_config_raw(struct qrexec_parsed_command *cmd,
257+
char **user)
258+
{
259259
const char *config_path = getenv("QUBES_RPC_CONFIG_PATH");
260260
if (!config_path)
261261
config_path = QUBES_RPC_CONFIG_PATH;
@@ -269,10 +269,31 @@ int load_service_config(const struct qrexec_parsed_command *cmd,
269269
config_full_path, sizeof(config_full_path), NULL);
270270
if (ret < 0)
271271
return 0;
272+
return qubes_toml_config_parse(config_full_path, &cmd->wait_for_session, user,
273+
&cmd->send_service_descriptor);
274+
}
272275

273-
return qubes_toml_config_parse(config_full_path, wait_for_session, user);
276+
int load_service_config_v2(struct qrexec_parsed_command *cmd) {
277+
assert(cmd->service_descriptor);
278+
char *tmp_user = NULL;
279+
int res = load_service_config_raw(cmd, &tmp_user);
280+
if (res >= 0 && tmp_user != NULL) {
281+
free(cmd->username);
282+
cmd->username = tmp_user;
283+
}
284+
return res;
274285
}
275286

287+
int load_service_config(struct qrexec_parsed_command *cmd,
288+
int *wait_for_session, char **user) {
289+
int rc = load_service_config_raw(cmd, user);
290+
if (rc >= 0) {
291+
*wait_for_session = cmd->wait_for_session;
292+
}
293+
return rc;
294+
}
295+
296+
276297
struct qrexec_parsed_command *parse_qubes_rpc_command(
277298
const char *cmdline, bool strip_username) {
278299

@@ -284,6 +305,7 @@ struct qrexec_parsed_command *parse_qubes_rpc_command(
284305
}
285306

286307
memset(cmd, 0, sizeof(*cmd));
308+
cmd->send_service_descriptor = true;
287309
cmd->cmdline = cmdline;
288310

289311
if (strip_username) {
@@ -469,9 +491,11 @@ static int execute_qrexec_service(
469491
*pid = 0;
470492
set_nonblock(s);
471493

472-
/* send part after "QUBESRPC ", including trailing NUL */
473-
const char *desc = cmd->command + RPC_REQUEST_COMMAND_LEN + 1;
474-
buffer_append(stdin_buffer, desc, strlen(desc) + 1);
494+
if (cmd->send_service_descriptor) {
495+
/* send part after "QUBESRPC ", including trailing NUL */
496+
const char *desc = cmd->command + RPC_REQUEST_COMMAND_LEN + 1;
497+
buffer_append(stdin_buffer, desc, strlen(desc) + 1);
498+
}
475499
return 0;
476500
}
477501

libqrexec/libqrexec-utils.h

+14-4
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ struct buffer {
5252
struct qrexec_parsed_command {
5353
const char *cmdline;
5454

55-
/* Username ("user", NULL when strip_username is false) */
55+
/* Username ("user", NULL when strip_username is false).
56+
* Always safe to pass to free(). */
5657
char *username;
5758

5859
/* Override to disable "wait for session" */
@@ -75,6 +76,12 @@ struct qrexec_parsed_command {
7576

7677
/* Source domain (the part after service name). */
7778
char *source_domain;
79+
80+
/* Should a session be waited for? */
81+
bool wait_for_session;
82+
83+
/* For socket-based services: Should the service descriptor be sent? */
84+
bool send_service_descriptor;
7885
};
7986

8087
/* Parse a command, return NULL on failure. Uses cmd->cmdline
@@ -89,9 +96,13 @@ void destroy_qrexec_parsed_command(struct qrexec_parsed_command *cmd);
8996
* 1 - config successfuly loaded
9097
* 0 - config not found
9198
* -1 - other error
99+
*
100+
* Deprecated, use load_service_config_v2() instead.
92101
*/
93-
int load_service_config(const struct qrexec_parsed_command *cmd_name,
94-
int *wait_for_session, char **user);
102+
int load_service_config(struct qrexec_parsed_command *cmd_name,
103+
int *wait_for_session, char **user)
104+
__attribute__((deprecated("use load_service_config_v2() instead")));
105+
int load_service_config_v2(struct qrexec_parsed_command *cmd);
95106

96107
typedef void (do_exec_t)(const char *cmdline, const char *user);
97108
void register_exec_func(do_exec_t *func);
@@ -303,7 +314,6 @@ void qrexec_log(int level, int errnoval, const char *file, int line,
303314
const char *func, const char *fmt, ...) __attribute__((format(printf, 6, 7)));
304315

305316
void setup_logging(const char *program_name);
306-
int qubes_toml_config_parse(const char *config_full_path, int *wait_for_session, char **user);
307317

308318
/**
309319
* Make an Admin API call to qubesd. The returned buffer must be released by

libqrexec/private.h

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#include <stdbool.h>
2+
int qubes_toml_config_parse(const char *config_full_path, bool *wait_for_session, char **user, bool *skip_service_descriptor);

0 commit comments

Comments
 (0)