Skip to content

Commit a893050

Browse files
committed
Clean up configuration loading
This technically breaks the libqrexec ABI by increasing the size of struct qrexec_parsed_command. However, nothing outside of libqrexec actually allocates instances of this struct, so adding new members at the end is harmless. It also changes a function that took a const pointer to instead take a non-const pointer, but this disappears at the ABI level. It also fixes some memory leaks. These memory leaks are small enough that they were likely not observed in practice, especially since they require invalid configuration files to trigger.
1 parent b5a3c3e commit a893050

File tree

6 files changed

+108
-62
lines changed

6 files changed

+108
-62
lines changed

agent/qrexec-agent.c

+2-10
Original file line numberDiff line numberDiff line change
@@ -592,21 +592,13 @@ static void handle_server_exec_request_init(struct msg_header *hdr)
592592

593593
/* load service config only for service requests */
594594
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) {
595+
if (load_service_config_v2(cmd) < 0) {
599596
LOG(ERROR, "Could not load config for command %s", buf);
600597
return;
601598
}
602599

603-
if (user != NULL) {
604-
free(cmd->username);
605-
cmd->username = user;
606-
}
607-
608600
/* "nogui:" prefix has priority */
609-
if (!cmd->nogui && wait_for_session && wait_for_session_maybe(cmd)) {
601+
if (!cmd->nogui && cmd->wait_for_session && wait_for_session_maybe(cmd)) {
610602
/* waiting for session, postpone actual call */
611603
int slot_index;
612604
for (slot_index = 0; slot_index < MAX_FDS; slot_index++)

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/exec.c

+24-5
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,10 @@ static int find_file(
252252
}
253253
return -1;
254254
}
255-
int load_service_config(const struct qrexec_parsed_command *cmd,
256-
int *wait_for_session, char **user) {
257-
assert(cmd->service_descriptor);
258-
assert(*user == NULL);
259255

256+
static int load_service_config_raw(struct qrexec_parsed_command *cmd,
257+
char **user)
258+
{
260259
const char *config_path = getenv("QUBES_RPC_CONFIG_PATH");
261260
if (!config_path)
262261
config_path = QUBES_RPC_CONFIG_PATH;
@@ -270,10 +269,30 @@ int load_service_config(const struct qrexec_parsed_command *cmd,
270269
config_full_path, sizeof(config_full_path), NULL);
271270
if (ret < 0)
272271
return 0;
272+
return qubes_toml_config_parse(config_full_path, &cmd->wait_for_session, user);
273+
}
274+
275+
int load_service_config_v2(struct qrexec_parsed_command *cmd) {
276+
assert(cmd->service_descriptor);
277+
char *tmp_user = NULL;
278+
int res = load_service_config_raw(cmd, &tmp_user);
279+
if (res >= 0 && tmp_user != NULL) {
280+
free(cmd->username);
281+
cmd->username = tmp_user;
282+
}
283+
return res;
284+
}
273285

274-
return qubes_toml_config_parse(config_full_path, wait_for_session, user);
286+
int load_service_config(struct qrexec_parsed_command *cmd,
287+
int *wait_for_session, char **user) {
288+
int rc = load_service_config_raw(cmd, user);
289+
if (rc >= 0) {
290+
*wait_for_session = cmd->wait_for_session;
291+
}
292+
return rc;
275293
}
276294

295+
277296
struct qrexec_parsed_command *parse_qubes_rpc_command(
278297
const char *cmdline, bool strip_username) {
279298

libqrexec/libqrexec-utils.h

+11-3
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,9 @@ 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;
7882
};
7983

8084
/* Parse a command, return NULL on failure. Uses cmd->cmdline
@@ -89,9 +93,13 @@ void destroy_qrexec_parsed_command(struct qrexec_parsed_command *cmd);
8993
* 1 - config successfuly loaded
9094
* 0 - config not found
9195
* -1 - other error
96+
*
97+
* Deprecated, use load_service_config_v2() instead.
9298
*/
93-
int load_service_config(const struct qrexec_parsed_command *cmd_name,
94-
int *wait_for_session, char **user);
99+
int load_service_config(struct qrexec_parsed_command *cmd_name,
100+
int *wait_for_session, char **user)
101+
__attribute__((deprecated("use load_service_config_v2() instead")));
102+
int load_service_config_v2(struct qrexec_parsed_command *cmd);
95103

96104
typedef void (do_exec_t)(const char *cmdline, const char *user);
97105
void register_exec_func(do_exec_t *func);

libqrexec/private.h

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

libqrexec/toml.c

+68-39
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,46 @@ static bool qubes_is_key_byte(unsigned char c) {
132132
}
133133
}
134134

135-
int qubes_toml_config_parse(const char *config_full_path, int *wait_for_session, char **user)
135+
static void toml_invalid_type(enum toml_type ty, const char *file, size_t lineno, const char *msg)
136+
{
137+
const char *bad_type;
138+
switch (ty) {
139+
case TOML_TYPE_INVALID:
140+
bad_type = "<invalid value>";
141+
break;
142+
case TOML_TYPE_BOOL:
143+
bad_type = "Boolean";
144+
break;
145+
case TOML_TYPE_INTEGER:
146+
bad_type = "Integer";
147+
break;
148+
case TOML_TYPE_STRING:
149+
bad_type = "String";
150+
break;
151+
default:
152+
abort();
153+
}
154+
LOG(ERROR, "%s:%zu: %s not valid for %s", file, lineno, bad_type, msg);
155+
}
156+
157+
static bool toml_check_dup_key(bool *seen_already, const char *file, size_t lineno, const char *msg)
158+
{
159+
if (*seen_already) {
160+
LOG(ERROR, "%s:%zu: Key %s already seen", file, lineno, msg);
161+
return true;
162+
}
163+
*seen_already = true;
164+
return false;
165+
}
166+
167+
static void toml_value_free(union toml_data *value, enum toml_type ty) {
168+
if (ty == TOML_TYPE_STRING) {
169+
free(value->string);
170+
value->string = NULL;
171+
}
172+
}
173+
174+
int qubes_toml_config_parse(const char *config_full_path, bool *wait_for_session, char **user)
136175
{
137176
int result = -1; /* assume problem */
138177
FILE *config_file = fopen(config_full_path, "re");
@@ -147,6 +186,21 @@ int qubes_toml_config_parse(const char *config_full_path, int *wait_for_session,
147186
ssize_t signed_linelen;
148187
bool seen_wait_for_session = false;
149188
bool seen_user = false;
189+
*wait_for_session = 0;
190+
#define CHECK_DUP_KEY(v) do { \
191+
if (toml_check_dup_key(&(v), config_full_path, lineno, current_line)) { \
192+
toml_value_free(&value, ty); \
193+
goto bad; \
194+
} \
195+
} while (0);
196+
#define CHECK_TYPE(v, msg) do { \
197+
if ((v) != ty) { \
198+
toml_invalid_type(v, config_full_path, lineno, msg); \
199+
toml_value_free(&value, ty); \
200+
goto bad; \
201+
} \
202+
} while (0);
203+
150204
while ((signed_linelen = getline(&current_line, &bufsize, config_file)) != -1) {
151205
lineno++;
152206
/* Other negative values are invalid. If nothing at all is read that means EOF. */
@@ -218,55 +272,30 @@ int qubes_toml_config_parse(const char *config_full_path, int *wait_for_session,
218272
current_line[key_len] = '\0';
219273
union toml_data value;
220274
enum toml_type ty = parse_toml_value(config_full_path, lineno, key_cursor, &value);
275+
if (ty == TOML_TYPE_INVALID)
276+
goto bad;
221277

222278
if (strcmp(current_line, "wait-for-session") == 0) {
223-
if (seen_wait_for_session) {
224-
LOG(ERROR, "%s:%zu: Key '%s' appears more than once", config_full_path, lineno, current_line);
225-
goto bad;
226-
}
227-
seen_wait_for_session = true;
228-
if (ty == TOML_TYPE_BOOL) {
229-
*wait_for_session = (int)value.boolean;
230-
continue;
231-
} else if (ty == TOML_TYPE_INTEGER) {
279+
CHECK_DUP_KEY(seen_wait_for_session);
280+
if (ty == TOML_TYPE_INTEGER) {
232281
if (value.integer < 2) {
233282
*wait_for_session = (int)value.integer;
234283
continue;
235284
}
236-
LOG(ERROR, "Integer value %llu used when a boolean was expected", value.integer);
237-
} else if (ty == TOML_TYPE_STRING) {
238-
LOG(ERROR, "String value '%s' not valid for 'wait-for-session'", value.string);
239-
free(value.string);
240-
value.string = NULL;
241-
}
242-
} else if (strcmp(current_line, "force-user") == 0) {
243-
if (seen_user) {
244-
LOG(ERROR, "%s:%zu: Key '%s' appears more than once", config_full_path, lineno, current_line);
245-
goto bad;
246-
}
247-
seen_user = true;
248-
char *bad_type;
249-
switch (ty) {
250-
case TOML_TYPE_INVALID:
285+
LOG(ERROR, "%s:%zu: Unsupported integer value %llu for boolean", config_full_path, lineno, value.integer);
251286
goto bad;
252-
case TOML_TYPE_BOOL:
253-
bad_type = "Boolean";
254-
break;
255-
case TOML_TYPE_INTEGER:
256-
bad_type = "Integer";
257-
break;
258-
case TOML_TYPE_STRING:
259-
*user = value.string;
260-
continue;
261-
default:
262-
abort();
287+
} else {
288+
CHECK_TYPE(TOML_TYPE_BOOL, "wait-for-session");
289+
*wait_for_session = value.boolean;
263290
}
264-
LOG(ERROR, "%s:%zu: %s not valid for user name or user ID", config_full_path, lineno, bad_type);
291+
} else if (strcmp(current_line, "force-user") == 0) {
292+
CHECK_DUP_KEY(seen_user);
293+
CHECK_TYPE(TOML_TYPE_STRING, "user name or user ID");
294+
*user = value.string;
265295
} else {
266296
LOG(ERROR, "%s:%zu: Unsupported key %s", config_full_path, lineno, current_line);
267-
continue;
297+
toml_value_free(&value, ty);
268298
}
269-
goto bad;
270299
}
271300

272301
result = 1;

0 commit comments

Comments
 (0)