Skip to content

Commit e33f334

Browse files
committed
qrexec-daemon: partially validate messages from client
This is not strictly necessary, as the client is trusted, but it's still good practice. The validation is cheap and debugging a buggy client without it would not be. The validation is limited to that needed to prevent memory unsafety in qrexec-daemon. It's not designed to protect the agent, which can do as much or as little validation as it likes.
1 parent ad72712 commit e33f334

File tree

1 file changed

+49
-20
lines changed

1 file changed

+49
-20
lines changed

daemon/qrexec-daemon.c

+49-20
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*
2020
*/
2121

22+
#include <inttypes.h>
2223
#include <stdio.h>
2324
#include <stdlib.h>
2425
#include <unistd.h>
@@ -534,18 +535,42 @@ static void release_vchan_port(int port, int expected_remote_id)
534535
static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
535536
{
536537
struct exec_params params;
537-
int len = hdr->len-sizeof(params);
538-
char buf[len];
538+
uint32_t len;
539+
char *buf = NULL;
539540
int use_default_user = 0;
540541
int i;
541542

543+
if (hdr->len <= sizeof(params)) {
544+
LOG(ERROR, "Too-short packet received from client %d: "
545+
"type %" PRIu32 ", len %" PRIu32 "(min %zu)",
546+
fd, hdr->type, hdr->len, sizeof(params) + 1);
547+
goto terminate;
548+
}
549+
if (hdr->len > MAX_QREXEC_CMD_LEN) {
550+
LOG(ERROR, "Too-long packet received from client %d: "
551+
"type %" PRIu32 ", len %" PRIu32 "(max %lu)",
552+
fd, hdr->type, hdr->len, MAX_QREXEC_CMD_LEN);
553+
goto terminate;
554+
}
555+
len = hdr->len - sizeof(params);
556+
557+
buf = malloc(len);
558+
if (buf == NULL) {
559+
PERROR("malloc");
560+
goto terminate;
561+
}
562+
542563
if (!read_all(fd, &params, sizeof(params))) {
543-
terminate_client(fd);
544-
return 0;
564+
goto terminate;
545565
}
546566
if (!read_all(fd, buf, len)) {
547-
terminate_client(fd);
548-
return 0;
567+
goto terminate;
568+
}
569+
570+
if (buf[len - 1] != '\0') {
571+
LOG(ERROR, "Client sent buffer of length %" PRIu32 " that is not "
572+
"NUL-terminated", len);
573+
goto terminate;
549574
}
550575

551576
if (hdr->type == MSG_SERVICE_CONNECT) {
@@ -562,8 +587,7 @@ static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
562587
if (i > policy_pending_max) {
563588
LOG(ERROR, "Connection with ident %s not requested or already handled",
564589
policy_pending[i].params.ident);
565-
terminate_client(fd);
566-
return 0;
590+
goto terminate;
567591
}
568592
policy_pending[i].response_sent = RESPONSE_ALLOW;
569593
}
@@ -574,29 +598,28 @@ static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
574598
params.connect_port = allocate_vchan_port(params.connect_domain);
575599
if (params.connect_port <= 0) {
576600
LOG(ERROR, "Failed to allocate new vchan port, too many clients?");
577-
terminate_client(fd);
578-
return 0;
601+
goto terminate;
579602
}
580603
/* notify the client when this connection got terminated */
581604
vchan_port_notify_client[params.connect_port-VCHAN_BASE_DATA_PORT] = fd;
582605
client_params.connect_port = params.connect_port;
583606
client_params.connect_domain = remote_domain_id;
584607
hdr->len = sizeof(client_params);
585-
if (!write_all(fd, hdr, sizeof(*hdr))) {
586-
terminate_client(fd);
587-
release_vchan_port(params.connect_port, params.connect_domain);
588-
return 0;
589-
}
590-
if (!write_all(fd, &client_params, sizeof(client_params))) {
608+
if (!write_all(fd, hdr, sizeof(*hdr)) ||
609+
!write_all(fd, &client_params, sizeof(client_params))) {
591610
terminate_client(fd);
592611
release_vchan_port(params.connect_port, params.connect_domain);
612+
free(buf);
593613
return 0;
594614
}
595615
/* restore original len value */
596616
hdr->len = len+sizeof(params);
597617
} else {
598-
assert(params.connect_port >= VCHAN_BASE_DATA_PORT);
599-
assert(params.connect_port < VCHAN_BASE_DATA_PORT+MAX_CLIENTS);
618+
if (!((params.connect_port >= VCHAN_BASE_DATA_PORT) &&
619+
(params.connect_port < VCHAN_BASE_DATA_PORT+MAX_CLIENTS))) {
620+
LOG(ERROR, "Invalid connect port %" PRIu32, params.connect_port);
621+
goto terminate;
622+
}
600623
}
601624

602625
if (!strncmp(buf, default_user_keyword, default_user_keyword_len_without_colon+1)) {
@@ -617,9 +640,14 @@ static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
617640
send_len) != send_len)
618641
handle_vchan_error("send buf");
619642
} else
620-
if (libvchan_send(vchan, buf, len) < len)
643+
if (libvchan_send(vchan, buf, len) < (int)len)
621644
handle_vchan_error("send buf");
645+
free(buf);
622646
return 1;
647+
terminate:
648+
terminate_client(fd);
649+
free(buf);
650+
return 0;
623651
}
624652

625653
static void handle_cmdline_message_from_client(int fd)
@@ -639,10 +667,11 @@ static void handle_cmdline_message_from_client(int fd)
639667
return;
640668
}
641669

642-
if (!handle_cmdline_body_from_client(fd, &hdr))
670+
if (!handle_cmdline_body_from_client(fd, &hdr)) {
643671
// client disconnected while sending cmdline, above call already
644672
// cleaned up client info
645673
return;
674+
}
646675
clients[fd].state = CLIENT_RUNNING;
647676
}
648677

0 commit comments

Comments
 (0)