qrexec: new communication scheme, agent<->server part
1) Instead of a set of predefined commands, we send MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING msg with a parameter (e.g. "org.qubes-os.vm.Filecopy") defining required action 2) qrexec_daemon just forks qrexec_policy, that will take care of actually allowing and executing required action 3) after MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING, qrexec_agent does not execute a command - it justs uses already established file descriptors to send data to/from. Thus, there is no need to use ~/.xxxxxspool - a command line tool can have direct access to remote fds.
This commit is contained in:
parent
732a90443e
commit
c05b26763a
@ -6,8 +6,8 @@ COMMONIOALL=../common/ioall.o
|
|||||||
all: qrexec_daemon qrexec_agent qrexec_client
|
all: qrexec_daemon qrexec_agent qrexec_client
|
||||||
qrexec_daemon: qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o buffer.o write_stdin.o
|
qrexec_daemon: qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o buffer.o write_stdin.o
|
||||||
$(CC) -pie -L../vchan -L../u2mfn -g -o qrexec_daemon qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o write_stdin.o buffer.o $(XENLIBS)
|
$(CC) -pie -L../vchan -L../u2mfn -g -o qrexec_daemon qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o write_stdin.o buffer.o $(XENLIBS)
|
||||||
qrexec_agent: qrexec_agent.o exec.o txrx-vchan.o write_stdin.o buffer.o $(COMMONIOALL)
|
qrexec_agent: qrexec_agent.o unix_server.o exec.o txrx-vchan.o write_stdin.o buffer.o $(COMMONIOALL)
|
||||||
$(CC) -pie -L../vchan -L../u2mfn -g -o qrexec_agent qrexec_agent.o exec.o txrx-vchan.o write_stdin.o buffer.o $(COMMONIOALL) $(XENLIBS)
|
$(CC) -pie -L../vchan -L../u2mfn -g -o qrexec_agent qrexec_agent.o unix_server.o exec.o txrx-vchan.o write_stdin.o buffer.o $(COMMONIOALL) $(XENLIBS)
|
||||||
qrexec_client: qrexec_client.o $(COMMONIOALL) exec.o
|
qrexec_client: qrexec_client.o $(COMMONIOALL) exec.o
|
||||||
$(CC) -pie -g -o qrexec_client qrexec_client.o $(COMMONIOALL) exec.o
|
$(CC) -pie -g -o qrexec_client qrexec_client.o $(COMMONIOALL) exec.o
|
||||||
clean:
|
clean:
|
||||||
|
@ -26,11 +26,13 @@
|
|||||||
#define REXEC_PORT 512
|
#define REXEC_PORT 512
|
||||||
|
|
||||||
#define QREXEC_AGENT_TRIGGER_PATH "/var/run/qubes/qrexec_agent"
|
#define QREXEC_AGENT_TRIGGER_PATH "/var/run/qubes/qrexec_agent"
|
||||||
|
#define QREXEC_AGENT_FDPASS_PATH "/var/run/qubes/qrexec_agent_fdpass"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MSG_CLIENT_TO_SERVER_EXEC_CMDLINE = 0x100,
|
MSG_CLIENT_TO_SERVER_EXEC_CMDLINE = 0x100,
|
||||||
MSG_CLIENT_TO_SERVER_JUST_EXEC,
|
MSG_CLIENT_TO_SERVER_JUST_EXEC,
|
||||||
|
|
||||||
|
MSG_SERVER_TO_AGENT_CONNECT_EXISTING,
|
||||||
MSG_SERVER_TO_AGENT_EXEC_CMDLINE,
|
MSG_SERVER_TO_AGENT_EXEC_CMDLINE,
|
||||||
MSG_SERVER_TO_AGENT_JUST_EXEC,
|
MSG_SERVER_TO_AGENT_JUST_EXEC,
|
||||||
MSG_SERVER_TO_AGENT_INPUT,
|
MSG_SERVER_TO_AGENT_INPUT,
|
||||||
@ -42,19 +44,13 @@ enum {
|
|||||||
MSG_AGENT_TO_SERVER_STDOUT,
|
MSG_AGENT_TO_SERVER_STDOUT,
|
||||||
MSG_AGENT_TO_SERVER_STDERR,
|
MSG_AGENT_TO_SERVER_STDERR,
|
||||||
MSG_AGENT_TO_SERVER_EXIT_CODE,
|
MSG_AGENT_TO_SERVER_EXIT_CODE,
|
||||||
MSG_AGENT_TO_SERVER_TRIGGER_EXEC,
|
MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING,
|
||||||
|
|
||||||
MSG_SERVER_TO_CLIENT_STDOUT,
|
MSG_SERVER_TO_CLIENT_STDOUT,
|
||||||
MSG_SERVER_TO_CLIENT_STDERR,
|
MSG_SERVER_TO_CLIENT_STDERR,
|
||||||
MSG_SERVER_TO_CLIENT_EXIT_CODE
|
MSG_SERVER_TO_CLIENT_EXIT_CODE
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
|
||||||
QREXEC_EXECUTE_FILE_COPY=0x700,
|
|
||||||
QREXEC_EXECUTE_FILE_COPY_FOR_DISPVM,
|
|
||||||
QREXEC_EXECUTE_APPMENUS_SYNC
|
|
||||||
};
|
|
||||||
|
|
||||||
struct server_header {
|
struct server_header {
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
unsigned int client_id;
|
unsigned int client_id;
|
||||||
@ -65,3 +61,13 @@ struct client_header {
|
|||||||
unsigned int type;
|
unsigned int type;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct connect_existing_params {
|
||||||
|
char ident[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct trigger_connect_params {
|
||||||
|
char exec_index[64];
|
||||||
|
char target_vmname[32];
|
||||||
|
struct connect_existing_params process_fds;
|
||||||
|
};
|
||||||
|
@ -68,12 +68,14 @@ struct _process_fd process_fd[MAX_FDS];
|
|||||||
struct _client_info client_info[MAX_FDS];
|
struct _client_info client_info[MAX_FDS];
|
||||||
|
|
||||||
int trigger_fd;
|
int trigger_fd;
|
||||||
|
int passfd_socket;
|
||||||
|
|
||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
peer_server_init(REXEC_PORT);
|
peer_server_init(REXEC_PORT);
|
||||||
umask(0);
|
umask(0);
|
||||||
mkfifo(QREXEC_AGENT_TRIGGER_PATH, 0666);
|
mkfifo(QREXEC_AGENT_TRIGGER_PATH, 0666);
|
||||||
|
passfd_socket = get_server_socket(QREXEC_AGENT_FDPASS_PATH);
|
||||||
umask(077);
|
umask(077);
|
||||||
trigger_fd =
|
trigger_fd =
|
||||||
open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK);
|
open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK);
|
||||||
@ -145,15 +147,9 @@ void handle_just_exec(int client_id, int len)
|
|||||||
fprintf(stderr, "executed (nowait) %s pid %d\n", buf, pid);
|
fprintf(stderr, "executed (nowait) %s pid %d\n", buf, pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_exec(int client_id, int len)
|
void create_info_about_client(int client_id, int pid, int stdin_fd,
|
||||||
|
int stdout_fd, int stderr_fd)
|
||||||
{
|
{
|
||||||
char buf[len];
|
|
||||||
int pid, stdin_fd, stdout_fd, stderr_fd;
|
|
||||||
|
|
||||||
read_all_vchan_ext(buf, len);
|
|
||||||
|
|
||||||
do_fork_exec(buf, &pid, &stdin_fd, &stdout_fd, &stderr_fd);
|
|
||||||
|
|
||||||
process_fd[stdout_fd].client_id = client_id;
|
process_fd[stdout_fd].client_id = client_id;
|
||||||
process_fd[stdout_fd].type = FDTYPE_STDOUT;
|
process_fd[stdout_fd].type = FDTYPE_STDOUT;
|
||||||
process_fd[stdout_fd].is_blocked = 0;
|
process_fd[stdout_fd].is_blocked = 0;
|
||||||
@ -177,11 +173,35 @@ void handle_exec(int client_id, int len)
|
|||||||
client_info[client_id].is_blocked = 0;
|
client_info[client_id].is_blocked = 0;
|
||||||
client_info[client_id].is_close_after_flush_needed = 0;
|
client_info[client_id].is_close_after_flush_needed = 0;
|
||||||
buffer_init(&client_info[client_id].buffer);
|
buffer_init(&client_info[client_id].buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_exec(int client_id, int len)
|
||||||
|
{
|
||||||
|
char buf[len];
|
||||||
|
int pid, stdin_fd, stdout_fd, stderr_fd;
|
||||||
|
|
||||||
|
read_all_vchan_ext(buf, len);
|
||||||
|
|
||||||
|
do_fork_exec(buf, &pid, &stdin_fd, &stdout_fd, &stderr_fd);
|
||||||
|
|
||||||
|
create_info_about_client(client_id, pid, stdin_fd, stdout_fd,
|
||||||
|
stderr_fd);
|
||||||
|
|
||||||
fprintf(stderr, "executed %s pid %d\n", buf, pid);
|
fprintf(stderr, "executed %s pid %d\n", buf, pid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handle_connect_existing(int client_id)
|
||||||
|
{
|
||||||
|
int stdin_fd, stdout_fd, stderr_fd;
|
||||||
|
struct connect_existing_params params;
|
||||||
|
read_all_vchan_ext(¶ms, sizeof(params));
|
||||||
|
sscanf(params.ident, "%d %d %d", &stdin_fd, &stdout_fd,
|
||||||
|
&stderr_fd);
|
||||||
|
create_info_about_client(client_id, 0, stdin_fd, stdout_fd,
|
||||||
|
stderr_fd);
|
||||||
|
client_info[client_id].is_exited = 1; //do not wait for SIGCHLD
|
||||||
|
}
|
||||||
|
|
||||||
void update_max_process_fd()
|
void update_max_process_fd()
|
||||||
{
|
{
|
||||||
@ -307,6 +327,9 @@ void handle_server_data()
|
|||||||
case MSG_XOFF:
|
case MSG_XOFF:
|
||||||
set_blocked_outerr(s_hdr.client_id, 1);
|
set_blocked_outerr(s_hdr.client_id, 1);
|
||||||
break;
|
break;
|
||||||
|
case MSG_SERVER_TO_AGENT_CONNECT_EXISTING:
|
||||||
|
handle_connect_existing(s_hdr.client_id);
|
||||||
|
break;
|
||||||
case MSG_SERVER_TO_AGENT_EXEC_CMDLINE:
|
case MSG_SERVER_TO_AGENT_EXEC_CMDLINE:
|
||||||
handle_exec(s_hdr.client_id, s_hdr.len);
|
handle_exec(s_hdr.client_id, s_hdr.len);
|
||||||
break;
|
break;
|
||||||
@ -432,6 +455,9 @@ int fill_fds_for_select(fd_set * rdset, fd_set * wrset)
|
|||||||
FD_SET(trigger_fd, rdset);
|
FD_SET(trigger_fd, rdset);
|
||||||
if (trigger_fd > max)
|
if (trigger_fd > max)
|
||||||
max = trigger_fd;
|
max = trigger_fd;
|
||||||
|
FD_SET(passfd_socket, rdset);
|
||||||
|
if (passfd_socket > max)
|
||||||
|
max = passfd_socket;
|
||||||
|
|
||||||
for (i = 0; i < MAX_FDS; i++)
|
for (i = 0; i < MAX_FDS; i++)
|
||||||
if (client_info[i].pid > 0 && client_info[i].is_blocked) {
|
if (client_info[i].pid > 0 && client_info[i].is_blocked) {
|
||||||
@ -467,28 +493,31 @@ void flush_client_data_agent(int client_id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handle_new_passfd()
|
||||||
|
{
|
||||||
|
int fd = do_accept(passfd_socket);
|
||||||
|
if (fd >= MAX_FDS) {
|
||||||
|
fprintf(stderr, "too many clients ?\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
// let client know what fd has been allocated
|
||||||
|
write(fd, &fd, sizeof(fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void handle_trigger_io()
|
void handle_trigger_io()
|
||||||
{
|
{
|
||||||
struct server_header s_hdr;
|
struct server_header s_hdr;
|
||||||
char buf[5];
|
struct trigger_connect_params params;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
s_hdr.client_id = 0;
|
s_hdr.client_id = 0;
|
||||||
s_hdr.len = 0;
|
s_hdr.len = 0;
|
||||||
if ((ret = read(trigger_fd, buf, 4)) == 4) {
|
ret = read(trigger_fd, ¶ms, sizeof(params));
|
||||||
buf[4] = 0;
|
if (ret == sizeof(params)) {
|
||||||
if (!strcmp(buf, "FCPR"))
|
s_hdr.type = MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING;
|
||||||
s_hdr.client_id = QREXEC_EXECUTE_FILE_COPY;
|
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||||
else if (!strcmp(buf, "DVMR"))
|
write_all_vchan_ext(¶ms, sizeof params);
|
||||||
s_hdr.client_id =
|
|
||||||
QREXEC_EXECUTE_FILE_COPY_FOR_DISPVM;
|
|
||||||
else if (!strcmp(buf, "SYNC"))
|
|
||||||
s_hdr.client_id =
|
|
||||||
QREXEC_EXECUTE_APPMENUS_SYNC;
|
|
||||||
if (s_hdr.client_id) {
|
|
||||||
s_hdr.type = MSG_AGENT_TO_SERVER_TRIGGER_EXEC;
|
|
||||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// trigger_fd is nonblock - so no need to reopen
|
// trigger_fd is nonblock - so no need to reopen
|
||||||
// not really, need to reopen at EOF
|
// not really, need to reopen at EOF
|
||||||
@ -518,6 +547,9 @@ int main()
|
|||||||
|
|
||||||
wait_for_vchan_or_argfd(max, &rdset, &wrset);
|
wait_for_vchan_or_argfd(max, &rdset, &wrset);
|
||||||
|
|
||||||
|
if (FD_ISSET(passfd_socket, &rdset))
|
||||||
|
handle_new_passfd();
|
||||||
|
|
||||||
while (read_ready_vchan_ext())
|
while (read_ready_vchan_ext())
|
||||||
handle_server_data();
|
handle_server_data();
|
||||||
|
|
||||||
|
@ -356,33 +356,27 @@ void check_children_count_and_wait_if_too_many()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ENSURE_NULL_TERMINATED(x) x[sizeof(x)-1] = 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Called when agent sends a message asking to execute a predefined command.
|
Called when agent sends a message asking to execute a predefined command.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void handle_execute_predefined_command(int req)
|
void handle_execute_predefined_command()
|
||||||
{
|
{
|
||||||
char *rcmd = NULL, *lcmd = NULL;
|
|
||||||
int i;
|
int i;
|
||||||
|
struct trigger_connect_params untrusted_params, params;
|
||||||
|
|
||||||
check_children_count_and_wait_if_too_many();
|
check_children_count_and_wait_if_too_many();
|
||||||
switch (req) {
|
read_all_vchan_ext(&untrusted_params, sizeof(params));
|
||||||
case QREXEC_EXECUTE_FILE_COPY:
|
|
||||||
rcmd = "directly:user:/usr/lib/qubes/qfile-agent";
|
/* sanitize start */
|
||||||
lcmd = "/usr/lib/qubes/qfile-daemon";
|
ENSURE_NULL_TERMINATED(untrusted_params.exec_index);
|
||||||
break;
|
ENSURE_NULL_TERMINATED(untrusted_params.target_vmname);
|
||||||
case QREXEC_EXECUTE_FILE_COPY_FOR_DISPVM:
|
ENSURE_NULL_TERMINATED(untrusted_params.process_fds.ident);
|
||||||
rcmd = "directly:user:/usr/lib/qubes/qfile-agent-dvm";
|
params = untrusted_params;
|
||||||
lcmd = "/usr/lib/qubes/qfile-daemon-dvm";
|
/* sanitize end */
|
||||||
break;
|
|
||||||
case QREXEC_EXECUTE_APPMENUS_SYNC:
|
|
||||||
rcmd = "user:grep -H = /usr/share/applications/*.desktop";
|
|
||||||
lcmd = "/usr/bin/qvm-sync-appmenus";
|
|
||||||
break;
|
|
||||||
default: /* cannot happen, already sanitized */
|
|
||||||
fprintf(stderr, "got trigger exec no %d\n", req);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
switch (fork()) {
|
switch (fork()) {
|
||||||
case -1:
|
case -1:
|
||||||
perror("fork");
|
perror("fork");
|
||||||
@ -397,8 +391,9 @@ void handle_execute_predefined_command(int req)
|
|||||||
close(i);
|
close(i);
|
||||||
signal(SIGCHLD, SIG_DFL);
|
signal(SIGCHLD, SIG_DFL);
|
||||||
signal(SIGPIPE, SIG_DFL);
|
signal(SIGPIPE, SIG_DFL);
|
||||||
execl("/usr/lib/qubes/qrexec_client", "qrexec_client", "-d",
|
execl("/usr/lib/qubes/qrexec_policy", "qrexec_policy",
|
||||||
remote_domain_name, "-l", lcmd, rcmd, NULL);
|
remote_domain_name, params.target_vmname,
|
||||||
|
params.exec_index, params.process_fds.ident, NULL);
|
||||||
perror("execl");
|
perror("execl");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -415,18 +410,8 @@ void check_client_id_in_range(unsigned int untrusted_client_id)
|
|||||||
|
|
||||||
void sanitize_message_from_agent(struct server_header *untrusted_header)
|
void sanitize_message_from_agent(struct server_header *untrusted_header)
|
||||||
{
|
{
|
||||||
int untrusted_cmd;
|
|
||||||
switch (untrusted_header->type) {
|
switch (untrusted_header->type) {
|
||||||
case MSG_AGENT_TO_SERVER_TRIGGER_EXEC:
|
case MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING:
|
||||||
untrusted_cmd = untrusted_header->client_id;
|
|
||||||
if (untrusted_cmd != QREXEC_EXECUTE_FILE_COPY &&
|
|
||||||
untrusted_cmd != QREXEC_EXECUTE_FILE_COPY_FOR_DISPVM &&
|
|
||||||
untrusted_cmd != QREXEC_EXECUTE_APPMENUS_SYNC) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"received MSG_AGENT_TO_SERVER_TRIGGER_EXEC cmd %d ?\n",
|
|
||||||
untrusted_cmd);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case MSG_AGENT_TO_SERVER_STDOUT:
|
case MSG_AGENT_TO_SERVER_STDOUT:
|
||||||
case MSG_AGENT_TO_SERVER_STDERR:
|
case MSG_AGENT_TO_SERVER_STDERR:
|
||||||
@ -465,8 +450,8 @@ void handle_message_from_agent()
|
|||||||
// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.client_id,
|
// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.client_id,
|
||||||
// s_hdr.len);
|
// s_hdr.len);
|
||||||
|
|
||||||
if (s_hdr.type == MSG_AGENT_TO_SERVER_TRIGGER_EXEC) {
|
if (s_hdr.type == MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING) {
|
||||||
handle_execute_predefined_command(s_hdr.client_id);
|
handle_execute_predefined_command();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user