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:
Rafal Wojtczuk 2011-07-04 18:56:56 +02:00
parent 732a90443e
commit c05b26763a
4 changed files with 89 additions and 66 deletions

View File

@ -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:

View File

@ -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;
};

View File

@ -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(&params, 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, &params, 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(&params, 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();

View File

@ -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;
} }