Merge branch 'qrexec-sockets3'
This commit is contained in:
		
						commit
						9bbfb4a567
					
				| @ -2,9 +2,11 @@ CC=gcc | ||||
| CFLAGS+=-I. -g -Wall -Wextra -Werror -pie -fPIC `pkg-config --cflags vchan-$(BACKEND_VMM)` | ||||
| LIBS=`pkg-config --libs vchan-$(BACKEND_VMM)` -lqrexec-utils | ||||
| 
 | ||||
| all: qrexec-agent qrexec-client-vm | ||||
| all: qrexec-agent qrexec-client-vm qrexec-fork-server | ||||
| qrexec-agent: qrexec-agent.o qrexec-agent-data.o | ||||
| 	$(CC) -pie -g -o qrexec-agent qrexec-agent.o qrexec-agent-data.o $(LIBS) | ||||
| qrexec-fork-server: qrexec-fork-server.o qrexec-agent-data.o | ||||
| 	$(CC) -pie -g -o qrexec-fork-server qrexec-fork-server.o qrexec-agent-data.o $(LIBS) | ||||
| qrexec-client-vm: qrexec-client-vm.o | ||||
| 	$(CC) -pie -g -o qrexec-client-vm qrexec-client-vm.o | ||||
| clean: | ||||
| @ -12,9 +14,10 @@ clean: | ||||
| 
 | ||||
| install: | ||||
| 	install -d $(DESTDIR)/etc/qubes-rpc | ||||
| 	install -d $(DESTDIR)/usr/lib/qubes | ||||
| 	install -d $(DESTDIR)/usr/lib/qubes $(DESTDIR)/usr/bin | ||||
| 	install qrexec-agent $(DESTDIR)/usr/lib/qubes | ||||
| 	install qrexec-client-vm $(DESTDIR)/usr/lib/qubes | ||||
| 	ln -s qrexec-client-vm $(DESTDIR)/usr/lib/qubes/qrexec_client_vm | ||||
| 	install qrexec-fork-server $(DESTDIR)/usr/bin | ||||
| 	install qubes-rpc-multiplexer $(DESTDIR)/usr/lib/qubes | ||||
| 
 | ||||
|  | ||||
| @ -29,6 +29,7 @@ | ||||
| #include <sys/stat.h> | ||||
| #include <sys/wait.h> | ||||
| #include <sys/select.h> | ||||
| #include <sys/socket.h> | ||||
| #include <fcntl.h> | ||||
| #include <libvchan.h> | ||||
| #include "qrexec.h" | ||||
| @ -38,52 +39,71 @@ | ||||
| #define VCHAN_BUFFER_SIZE 65536 | ||||
| 
 | ||||
| static volatile int child_exited; | ||||
| static volatile int stdio_socket_requested; | ||||
| int stdout_msg_type = MSG_DATA_STDOUT; | ||||
| pid_t child_process_pid; | ||||
| 
 | ||||
| static void sigchld_handler(int __attribute__((__unused__))x) | ||||
| { | ||||
| 	child_exited = 1; | ||||
| 	signal(SIGCHLD, sigchld_handler); | ||||
|     child_exited = 1; | ||||
|     signal(SIGCHLD, sigchld_handler); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void no_colon_in_cmd() | ||||
| static void sigusr1_handler(int __attribute__((__unused__))x) | ||||
| { | ||||
| 	fprintf(stderr, | ||||
| 		"cmdline is supposed to be in user:command form\n"); | ||||
| 	exit(1); | ||||
|     stdio_socket_requested = 1; | ||||
|     signal(SIGUSR1, SIG_IGN); | ||||
| } | ||||
| 
 | ||||
| void do_exec(char *cmd) | ||||
| int handle_handshake(libvchan_t *ctrl) | ||||
| { | ||||
| 	char buf[strlen(QUBES_RPC_MULTIPLEXER_PATH) + strlen(cmd) - strlen(RPC_REQUEST_COMMAND) + 1]; | ||||
| 	char *realcmd = index(cmd, ':'); | ||||
| 	if (!realcmd) | ||||
| 		no_colon_in_cmd(); | ||||
| 	/* mark end of username and move to command */ | ||||
| 	*realcmd = 0; | ||||
| 	realcmd++; | ||||
| 	/* ignore "nogui:" prefix in linux agent */ | ||||
| 	if (strncmp(realcmd, "nogui:", 6) == 0) | ||||
| 		realcmd+=6; | ||||
| 	/* replace magic RPC cmd with RPC multiplexer path */ | ||||
| 	if (strncmp(realcmd, RPC_REQUEST_COMMAND " ", strlen(RPC_REQUEST_COMMAND)+1)==0) { | ||||
| 		strcpy(buf, QUBES_RPC_MULTIPLEXER_PATH); | ||||
| 		strcpy(buf + strlen(QUBES_RPC_MULTIPLEXER_PATH), realcmd + strlen(RPC_REQUEST_COMMAND)); | ||||
| 		realcmd = buf; | ||||
| 	} | ||||
| 	signal(SIGCHLD, SIG_DFL); | ||||
| 	signal(SIGPIPE, SIG_DFL); | ||||
|     struct msg_header hdr; | ||||
|     struct peer_info info; | ||||
| 
 | ||||
| 	execl("/bin/su", "su", "-", cmd, "-c", realcmd, NULL); | ||||
| 	perror("execl"); | ||||
| 	exit(1); | ||||
|     /* send own HELLO */ | ||||
|     hdr.type = MSG_HELLO; | ||||
|     hdr.len = sizeof(info); | ||||
|     info.version = QREXEC_PROTOCOL_VERSION; | ||||
| 
 | ||||
|     if (libvchan_send(ctrl, &hdr, sizeof(hdr)) != sizeof(hdr)) { | ||||
|         fprintf(stderr, "Failed to send HELLO hdr to agent\n"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (libvchan_send(ctrl, &info, sizeof(info)) != sizeof(info)) { | ||||
|         fprintf(stderr, "Failed to send HELLO hdr to agent\n"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     /* receive MSG_HELLO from remote */ | ||||
|     if (libvchan_recv(ctrl, &hdr, sizeof(hdr)) != sizeof(hdr)) { | ||||
|         fprintf(stderr, "Failed to read agent HELLO hdr\n"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (hdr.type != MSG_HELLO || hdr.len != sizeof(info)) { | ||||
|         fprintf(stderr, "Invalid HELLO packet received: type %d, len %d\n", hdr.type, hdr.len); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (libvchan_recv(ctrl, &info, sizeof(info)) != sizeof(info)) { | ||||
|         fprintf(stderr, "Failed to read agent HELLO body\n"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (info.version != QREXEC_PROTOCOL_VERSION) { | ||||
|         fprintf(stderr, "Incompatible agent protocol version (remote %d, local %d)\n", info.version, QREXEC_PROTOCOL_VERSION); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int handle_just_exec(char *cmdline) | ||||
| { | ||||
| 	int fdn, pid; | ||||
|     int fdn, pid; | ||||
| 
 | ||||
|     switch (pid = fork()) { | ||||
|         case -1: | ||||
| @ -97,20 +117,20 @@ int handle_just_exec(char *cmdline) | ||||
|             exit(1); | ||||
|         default:; | ||||
|     } | ||||
| 	fprintf(stderr, "executed (nowait) %s pid %d\n", cmdline, pid); | ||||
|     fprintf(stderr, "executed (nowait) %s pid %d\n", cmdline, pid); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| void send_exit_code(libvchan_t *data_vchan, int status) | ||||
| { | ||||
| 	struct msg_header hdr; | ||||
| 	hdr.type = MSG_DATA_EXIT_CODE; | ||||
| 	hdr.len = sizeof(status); | ||||
| 	if (libvchan_send(data_vchan, &hdr, sizeof(hdr)) < 0) | ||||
| 		handle_vchan_error("write hdr"); | ||||
| 	if (libvchan_send(data_vchan, &status, sizeof(status)) < 0) | ||||
| 		handle_vchan_error("write status"); | ||||
| 	fprintf(stderr, "send exit code %d\n", status); | ||||
|     struct msg_header hdr; | ||||
|     hdr.type = MSG_DATA_EXIT_CODE; | ||||
|     hdr.len = sizeof(status); | ||||
|     if (libvchan_send(data_vchan, &hdr, sizeof(hdr)) < 0) | ||||
|         handle_vchan_error("write hdr"); | ||||
|     if (libvchan_send(data_vchan, &status, sizeof(status)) < 0) | ||||
|         handle_vchan_error("write status"); | ||||
|     fprintf(stderr, "send exit code %d\n", status); | ||||
| } | ||||
| 
 | ||||
| /* handle data from specified FD and send over vchan link
 | ||||
| @ -146,7 +166,10 @@ int handle_input(libvchan_t *vchan, int fd, int msg_type) | ||||
|             return -1; | ||||
| 
 | ||||
|         if (len == 0) { | ||||
|             close(fd); | ||||
|             if (shutdown(fd, SHUT_RD) < 0) { | ||||
|                 if (errno == ENOTSOCK) | ||||
|                     close(fd); | ||||
|             } | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| @ -188,14 +211,20 @@ int handle_remote_data(libvchan_t *data_vchan, int stdin_fd) | ||||
|                     /* discard the data */ | ||||
|                     continue; | ||||
|                 if (hdr.len == 0) { | ||||
|                     close(stdin_fd); | ||||
|                     if (shutdown(stdin_fd, SHUT_WR) < 0) { | ||||
|                         if (errno == ENOTSOCK) | ||||
|                             close(stdin_fd); | ||||
|                     } | ||||
|                     stdin_fd = -1; | ||||
|                     return 0; | ||||
|                 } else { | ||||
|                     /* FIXME: use buffered write here to prevent deadlock */ | ||||
|                     if (!write_all(stdin_fd, buf, hdr.len)) { | ||||
|                         if (errno == EPIPE) { | ||||
|                             close(stdin_fd); | ||||
|                         if (errno == EPIPE || errno == ECONNRESET) { | ||||
|                             if (shutdown(stdin_fd, SHUT_WR) < 0) { | ||||
|                                 if (errno == ENOTSOCK) | ||||
|                                     close(stdin_fd); | ||||
|                             } | ||||
|                             stdin_fd = -1; | ||||
|                         } else { | ||||
|                             perror("write"); | ||||
| @ -248,7 +277,10 @@ void process_child_io(libvchan_t *data_vchan, | ||||
|                 if (pid == child_process_pid) { | ||||
|                     child_process_status = WEXITSTATUS(status); | ||||
|                     if (stdin_fd >= 0) { | ||||
|                         close(stdin_fd); | ||||
|                         if (shutdown(stdin_fd, SHUT_WR) < 0) { | ||||
|                             if (errno == ENOTSOCK) | ||||
|                                 close(stdin_fd); | ||||
|                         } | ||||
|                         stdin_fd = -1; | ||||
|                     } | ||||
|                 } | ||||
| @ -264,6 +296,13 @@ void process_child_io(libvchan_t *data_vchan, | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         /* child signaled desire to use single socket for both stdin and stdout */ | ||||
|         if (stdio_socket_requested) { | ||||
|             if (stdout_fd != -1) | ||||
|                 close(stdout_fd); | ||||
|             stdout_fd = stdin_fd; | ||||
|             stdio_socket_requested = 0; | ||||
|         } | ||||
|         /* otherwise handle the events */ | ||||
| 
 | ||||
|         FD_ZERO(&rdset); | ||||
| @ -337,7 +376,10 @@ void process_child_io(libvchan_t *data_vchan, | ||||
|                 break; | ||||
|             case -2: | ||||
|                 /* remote process exited, no sense in sending more data to it */ | ||||
|                 close(stdout_fd); | ||||
|                 if (shutdown(stdout_fd, SHUT_RD) < 0) { | ||||
|                     if (errno == ENOTSOCK) | ||||
|                         close(stdout_fd); | ||||
|                 } | ||||
|                 stdout_fd = -1; | ||||
|                 close(stderr_fd); | ||||
|                 stderr_fd = -1; | ||||
| @ -353,6 +395,7 @@ pid_t handle_new_process(int type, int connect_domain, int connect_port, | ||||
|     libvchan_t *data_vchan; | ||||
|     pid_t pid; | ||||
|     int stdin_fd, stdout_fd, stderr_fd; | ||||
|     char pid_s[10]; | ||||
| 
 | ||||
|     if (type == MSG_SERVICE_CONNECT) { | ||||
|         if (cmdline_len != sizeof(*svc_params)) { | ||||
| @ -394,6 +437,10 @@ pid_t handle_new_process(int type, int connect_domain, int connect_port, | ||||
|     handle_handshake(data_vchan); | ||||
| 
 | ||||
|     signal(SIGCHLD, sigchld_handler); | ||||
|     signal(SIGUSR1, sigusr1_handler); | ||||
|     snprintf(pid_s, sizeof(pid_s), "%d", getpid()); | ||||
|     setenv("QREXEC_AGENT_PID", pid_s, 1); | ||||
|     /* TODO: use setresuid to allow child process to actually send the signal? */ | ||||
| 
 | ||||
|     switch (type) { | ||||
|         case MSG_JUST_EXEC: | ||||
|  | ||||
| @ -19,7 +19,10 @@ | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE | ||||
| #include <sys/select.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/un.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <signal.h> | ||||
| @ -38,7 +41,8 @@ | ||||
| #include "qrexec-agent.h" | ||||
| 
 | ||||
| struct _connection_info { | ||||
|     int pid; | ||||
|     int pid; /* pid of child process handling the data */ | ||||
|     int fd;  /* socket to process handling the data (wait for EOF here) */ | ||||
|     int connect_domain; | ||||
|     int connect_port; | ||||
| }; | ||||
| @ -55,7 +59,38 @@ int passfd_socket; | ||||
| 
 | ||||
| int meminfo_write_started = 0; | ||||
| 
 | ||||
| void do_exec(const char *cmd); | ||||
| void no_colon_in_cmd() | ||||
| { | ||||
|     fprintf(stderr, | ||||
|             "cmdline is supposed to be in user:command form\n"); | ||||
|     exit(1); | ||||
| } | ||||
| 
 | ||||
| void do_exec(const char *cmd) | ||||
| { | ||||
|     char buf[strlen(QUBES_RPC_MULTIPLEXER_PATH) + strlen(cmd) - strlen(RPC_REQUEST_COMMAND) + 1]; | ||||
|     char *realcmd = index(cmd, ':'); | ||||
|     if (!realcmd) | ||||
|         no_colon_in_cmd(); | ||||
|     /* mark end of username and move to command */ | ||||
|     *realcmd = 0; | ||||
|     realcmd++; | ||||
|     /* ignore "nogui:" prefix in linux agent */ | ||||
|     if (strncmp(realcmd, "nogui:", 6) == 0) | ||||
|         realcmd+=6; | ||||
|     /* replace magic RPC cmd with RPC multiplexer path */ | ||||
|     if (strncmp(realcmd, RPC_REQUEST_COMMAND " ", strlen(RPC_REQUEST_COMMAND)+1)==0) { | ||||
|         strcpy(buf, QUBES_RPC_MULTIPLEXER_PATH); | ||||
|         strcpy(buf + strlen(QUBES_RPC_MULTIPLEXER_PATH), realcmd + strlen(RPC_REQUEST_COMMAND)); | ||||
|         realcmd = buf; | ||||
|     } | ||||
|     signal(SIGCHLD, SIG_DFL); | ||||
|     signal(SIGPIPE, SIG_DFL); | ||||
| 
 | ||||
|     execl("/bin/su", "su", "-", cmd, "-c", realcmd, NULL); | ||||
|     perror("execl"); | ||||
|     exit(1); | ||||
| } | ||||
| 
 | ||||
| void handle_vchan_error(const char *op) | ||||
| { | ||||
| @ -63,51 +98,6 @@ void handle_vchan_error(const char *op) | ||||
|     exit(1); | ||||
| } | ||||
| 
 | ||||
| int handle_handshake(libvchan_t *ctrl) | ||||
| { | ||||
|     struct msg_header hdr; | ||||
|     struct peer_info info; | ||||
| 
 | ||||
|     /* send own HELLO */ | ||||
|     hdr.type = MSG_HELLO; | ||||
|     hdr.len = sizeof(info); | ||||
|     info.version = QREXEC_PROTOCOL_VERSION; | ||||
| 
 | ||||
|     if (libvchan_send(ctrl, &hdr, sizeof(hdr)) != sizeof(hdr)) { | ||||
|         fprintf(stderr, "Failed to send HELLO hdr to agent\n"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (libvchan_send(ctrl, &info, sizeof(info)) != sizeof(info)) { | ||||
|         fprintf(stderr, "Failed to send HELLO hdr to agent\n"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     /* receive MSG_HELLO from remote */ | ||||
|     if (libvchan_recv(ctrl, &hdr, sizeof(hdr)) != sizeof(hdr)) { | ||||
|         fprintf(stderr, "Failed to read agent HELLO hdr\n"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (hdr.type != MSG_HELLO || hdr.len != sizeof(info)) { | ||||
|         fprintf(stderr, "Invalid HELLO packet received: type %d, len %d\n", hdr.type, hdr.len); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (libvchan_recv(ctrl, &info, sizeof(info)) != sizeof(info)) { | ||||
|         fprintf(stderr, "Failed to read agent HELLO body\n"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (info.version != QREXEC_PROTOCOL_VERSION) { | ||||
|         fprintf(stderr, "Incompatible agent protocol version (remote %d, local %d)\n", info.version, QREXEC_PROTOCOL_VERSION); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| void init() | ||||
| { | ||||
|     /* FIXME: This 0 is remote domain ID */ | ||||
| @ -161,13 +151,70 @@ void wake_meminfo_writer() | ||||
|     meminfo_write_started = 1; | ||||
| } | ||||
| 
 | ||||
| void register_vchan_connection(pid_t pid, int domain, int port) | ||||
| int try_fork_server(int type, int connect_domain, int connect_port, | ||||
|         char *cmdline, int cmdline_len) { | ||||
|     char username[cmdline_len]; | ||||
|     char *colon; | ||||
|     char *fork_server_socket_path; | ||||
|     int s, len; | ||||
|     struct sockaddr_un remote; | ||||
|     struct qrexec_cmd_info info; | ||||
| 
 | ||||
|     strncpy(username, cmdline, cmdline_len); | ||||
|     colon = index(username, ':'); | ||||
|     if (!colon) | ||||
|         return -1; | ||||
|     *colon = '\0'; | ||||
| 
 | ||||
|     if (asprintf(&fork_server_socket_path, QREXEC_FORK_SERVER_SOCKET, username) < 0) { | ||||
|         fprintf(stderr, "Memory allocation failed\n"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     remote.sun_family = AF_UNIX; | ||||
|     strncpy(remote.sun_path, fork_server_socket_path, | ||||
|             sizeof(remote.sun_path)); | ||||
|     free(fork_server_socket_path); | ||||
| 
 | ||||
|     if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { | ||||
|         perror("socket"); | ||||
|         return -1; | ||||
|     } | ||||
|     len = strlen(remote.sun_path) + sizeof(remote.sun_family); | ||||
|     if (connect(s, (struct sockaddr *) &remote, len) == -1) { | ||||
|         if (errno != ECONNREFUSED) | ||||
|             perror("connect"); | ||||
|         close(s); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     info.type = type; | ||||
|     info.connect_domain = connect_domain; | ||||
|     info.connect_port = connect_port; | ||||
|     info.cmdline_len = cmdline_len-(strlen(username)+1); | ||||
|     if (!write_all(s, &info, sizeof(info))) { | ||||
|         perror("write"); | ||||
|         close(s); | ||||
|         return -1; | ||||
|     } | ||||
|     if (!write_all(s, colon+1, info.cmdline_len)) { | ||||
|         perror("write"); | ||||
|         close(s); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     return s; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void register_vchan_connection(pid_t pid, int fd, int domain, int port) | ||||
| { | ||||
|     int i; | ||||
| 
 | ||||
|     for (i = 0; i < MAX_FDS; i++) { | ||||
|         if (connection_info[i].pid == 0) { | ||||
|             connection_info[i].pid = pid; | ||||
|             connection_info[i].fd = fd; | ||||
|             connection_info[i].connect_domain = domain; | ||||
|             connection_info[i].connect_port = port; | ||||
|             return; | ||||
| @ -190,11 +237,23 @@ void handle_server_exec_request(struct msg_header *hdr) | ||||
|     if (libvchan_recv(ctrl_vchan, buf, hdr->len-sizeof(params)) < 0) | ||||
|         handle_vchan_error("read exec cmd"); | ||||
| 
 | ||||
|     if ((hdr->type == MSG_EXEC_CMDLINE || hdr->type == MSG_JUST_EXEC) && | ||||
|             !strstr(buf, ":nogui:")) { | ||||
|         int child_socket = try_fork_server(hdr->type, | ||||
|                 params.connect_domain, params.connect_port, | ||||
|                 buf, hdr->len-sizeof(params)); | ||||
|         if (child_socket >= 0) { | ||||
|             register_vchan_connection(-1, child_socket, | ||||
|                     params.connect_domain, params.connect_port); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     child_agent = handle_new_process(hdr->type, | ||||
|             params.connect_domain, params.connect_port, | ||||
|             buf, hdr->len-sizeof(params)); | ||||
| 
 | ||||
|     register_vchan_connection(child_agent, | ||||
|     register_vchan_connection(child_agent, -1, | ||||
|             params.connect_domain, params.connect_port); | ||||
| } | ||||
| 
 | ||||
| @ -262,27 +321,31 @@ int find_connection(int pid) | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| void release_connection(int id) { | ||||
|     struct msg_header hdr; | ||||
|     struct exec_params params; | ||||
| 
 | ||||
|     hdr.type = MSG_CONNECTION_TERMINATED; | ||||
|     hdr.len = sizeof(struct exec_params); | ||||
|     params.connect_domain = connection_info[id].connect_domain; | ||||
|     params.connect_port = connection_info[id].connect_port; | ||||
|     if (libvchan_send(ctrl_vchan, &hdr, sizeof(hdr)) < 0) | ||||
|         handle_vchan_error("send"); | ||||
|     if (libvchan_send(ctrl_vchan, ¶ms, sizeof(params)) < 0) | ||||
|         handle_vchan_error("send"); | ||||
|     connection_info[id].pid = 0; | ||||
| } | ||||
| 
 | ||||
| void reap_children() | ||||
| { | ||||
|     int status; | ||||
|     int pid; | ||||
|     int id; | ||||
|     struct msg_header hdr; | ||||
|     struct exec_params params; | ||||
|     while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { | ||||
|         id = find_connection(pid); | ||||
|         if (id < 0) | ||||
|             continue; | ||||
|         hdr.type = MSG_CONNECTION_TERMINATED; | ||||
|         hdr.len = sizeof(struct exec_params); | ||||
|         params.connect_domain = connection_info[id].connect_domain; | ||||
|         params.connect_port = connection_info[id].connect_port; | ||||
|         if (libvchan_send(ctrl_vchan, &hdr, sizeof(hdr)) < 0) | ||||
|             handle_vchan_error("send"); | ||||
|         if (libvchan_send(ctrl_vchan, ¶ms, sizeof(params)) < 0) | ||||
|             handle_vchan_error("send"); | ||||
|         connection_info[id].pid = 0; | ||||
|         release_connection(id); | ||||
|     } | ||||
|     child_exited = 0; | ||||
| } | ||||
| @ -290,6 +353,7 @@ void reap_children() | ||||
| int fill_fds_for_select(fd_set * rdset, fd_set * wrset) | ||||
| { | ||||
|     int max = -1; | ||||
|     int i; | ||||
|     FD_ZERO(rdset); | ||||
|     FD_ZERO(wrset); | ||||
| 
 | ||||
| @ -299,6 +363,14 @@ int fill_fds_for_select(fd_set * rdset, fd_set * wrset) | ||||
|     FD_SET(passfd_socket, rdset); | ||||
|     if (passfd_socket > max) | ||||
|         max = passfd_socket; | ||||
| 
 | ||||
|     for (i = 0; i < MAX_FDS; i++) { | ||||
|         if (connection_info[i].pid != 0 && connection_info[i].fd != -1) { | ||||
|             FD_SET(connection_info[i].fd, rdset); | ||||
|             if (connection_info[i].fd > max) | ||||
|                 max = connection_info[i].fd; | ||||
|         } | ||||
|     } | ||||
|     return max; | ||||
| } | ||||
| 
 | ||||
| @ -339,6 +411,24 @@ void handle_trigger_io() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void handle_terminated_fork_client(fd_set *rdset) { | ||||
|     int i, ret; | ||||
|     char buf[2]; | ||||
| 
 | ||||
|     for (i = 0; i < MAX_FDS; i++) { | ||||
|         if (connection_info[i].pid && connection_info[i].fd >= 0 && | ||||
|                 FD_ISSET(connection_info[i].fd, rdset)) { | ||||
|             ret = read(connection_info[i].fd, buf, sizeof(buf)); | ||||
|             if (ret == 0 || (ret == -1 && errno == ECONNRESET)) { | ||||
|                 close(connection_info[i].fd); | ||||
|                 release_connection(i); | ||||
|             } else { | ||||
|                 fprintf(stderr, "Unexpected read on fork-server connection: %d(%s)\n", ret, strerror(errno)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int main() | ||||
| { | ||||
|     fd_set rdset, wrset; | ||||
| @ -372,5 +462,7 @@ int main() | ||||
| 
 | ||||
|         if (FD_ISSET(trigger_fd, &rdset)) | ||||
|             handle_trigger_io(); | ||||
| 
 | ||||
|         handle_terminated_fork_client(&rdset); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -19,9 +19,20 @@ | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #define QREXEC_FORK_SERVER_SOCKET "/var/run/qubes/qrexec-server.%s.sock" | ||||
| 
 | ||||
| int handle_handshake(libvchan_t *ctrl); | ||||
| void handle_vchan_error(const char *op); | ||||
| void do_exec(const char *cmd); | ||||
| 
 | ||||
| pid_t handle_new_process(int type, | ||||
|         int connect_domain, int connect_port, | ||||
|         char *cmdline, int cmdline_len); | ||||
| 
 | ||||
| struct qrexec_cmd_info { | ||||
| 	int type; | ||||
| 	int connect_domain; | ||||
| 	int connect_port; | ||||
| 	int cmdline_len; | ||||
| 	char cmdline[0]; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										110
									
								
								qrexec/qrexec-fork-server.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								qrexec/qrexec-fork-server.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | ||||
| /*
 | ||||
|  * The Qubes OS Project, http://www.qubes-os.org
 | ||||
|  * | ||||
|  * Copyright (C) 2015  Marek Marczykowski-Górecki  <marmarek@invisiblethingslab.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * as published by the Free Software Foundation; either version 2 | ||||
|  * of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <signal.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/un.h> | ||||
| #include "qrexec.h" | ||||
| #include <libvchan.h> | ||||
| #include "libqrexec-utils.h" | ||||
| #include "qrexec-agent.h" | ||||
| 
 | ||||
| void do_exec(const char *cmd) | ||||
| { | ||||
| 	char buf[strlen(QUBES_RPC_MULTIPLEXER_PATH) + strlen(cmd) - strlen(RPC_REQUEST_COMMAND) + 1]; | ||||
| 	/* replace magic RPC cmd with RPC multiplexer path */ | ||||
| 	if (strncmp(cmd, RPC_REQUEST_COMMAND " ", strlen(RPC_REQUEST_COMMAND)+1)==0) { | ||||
| 		strcpy(buf, QUBES_RPC_MULTIPLEXER_PATH); | ||||
| 		strcpy(buf + strlen(QUBES_RPC_MULTIPLEXER_PATH), cmd + strlen(RPC_REQUEST_COMMAND)); | ||||
| 		cmd = buf; | ||||
| 	} | ||||
| 	signal(SIGCHLD, SIG_DFL); | ||||
| 	signal(SIGPIPE, SIG_DFL); | ||||
| 
 | ||||
| 	execl("/bin/sh", "sh", "-c", cmd, NULL); | ||||
| 	perror("execl"); | ||||
| 	exit(1); | ||||
| } | ||||
| 
 | ||||
| void handle_vchan_error(const char *op) | ||||
| { | ||||
|     fprintf(stderr, "Error while vchan %s, exiting\n", op); | ||||
|     exit(1); | ||||
| } | ||||
| 
 | ||||
| void handle_single_command(int fd, struct qrexec_cmd_info *info) { | ||||
| 	char cmdline[info->cmdline_len+1]; | ||||
| 
 | ||||
| 	if (!read_all(fd, cmdline, info->cmdline_len)) | ||||
| 		return; | ||||
| 	cmdline[info->cmdline_len] = 0; | ||||
| 
 | ||||
| 	handle_new_process(info->type, info->connect_domain, | ||||
| 			info->connect_port, | ||||
| 			cmdline, info->cmdline_len); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char **argv) { | ||||
| 	int s, fd; | ||||
| 	char *socket_path; | ||||
| 	struct qrexec_cmd_info info; | ||||
| 	struct sockaddr_un peer; | ||||
| 	unsigned int addrlen; | ||||
| 
 | ||||
| 
 | ||||
| 	if (argc == 2) { | ||||
| 		socket_path = argv[1]; | ||||
| 	} else if (argc == 1) { | ||||
| 		/* this will be leaked, but we don't care as the process will then terminate */ | ||||
| 		if (asprintf(&socket_path, QREXEC_FORK_SERVER_SOCKET, getenv("USER")) < 0) { | ||||
| 			fprintf(stderr, "Memory allocation failed\n"); | ||||
| 			exit(1); | ||||
| 		} | ||||
| 	} else { | ||||
| 		fprintf(stderr, "Usage: %s [socket path]\n", argv[0]); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 
 | ||||
| 	s = get_server_socket(socket_path); | ||||
| 	if (fcntl(s, F_SETFD, O_CLOEXEC) < 0) { | ||||
| 		perror("fcntl"); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 	signal(SIGCHLD, SIG_IGN); | ||||
|     register_exec_func(do_exec); | ||||
| 
 | ||||
| 	while ((fd = accept(s, (struct sockaddr *) &peer, &addrlen)) >= 0) { | ||||
| 		if (read_all(fd, &info, sizeof(info))) { | ||||
| 			handle_single_command(fd, &info); | ||||
| 		} | ||||
| 		close(fd); | ||||
| 		addrlen = sizeof(peer); | ||||
| 	} | ||||
| 	close(s); | ||||
| 	unlink(socket_path); | ||||
| 	return 0; | ||||
| } | ||||
| @ -400,6 +400,7 @@ rm -f %{name}-%{version} | ||||
| /usr/bin/qvm-mru-entry | ||||
| /usr/bin/xenstore-watch-qubes | ||||
| /usr/bin/qubes-desktop-run | ||||
| /usr/bin/qrexec-fork-server | ||||
| %dir /usr/lib/qubes | ||||
| /usr/lib/qubes/vusb-ctl.py* | ||||
| /usr/lib/qubes/dispvm-prerun.sh | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Marek Marczykowski-Górecki
						Marek Marczykowski-Górecki