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)` | CFLAGS+=-I. -g -Wall -Wextra -Werror -pie -fPIC `pkg-config --cflags vchan-$(BACKEND_VMM)` | ||||||
| LIBS=`pkg-config --libs vchan-$(BACKEND_VMM)` -lqrexec-utils | 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 | qrexec-agent: qrexec-agent.o qrexec-agent-data.o | ||||||
| 	$(CC) -pie -g -o qrexec-agent qrexec-agent.o qrexec-agent-data.o $(LIBS) | 	$(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 | qrexec-client-vm: qrexec-client-vm.o | ||||||
| 	$(CC) -pie -g -o qrexec-client-vm qrexec-client-vm.o | 	$(CC) -pie -g -o qrexec-client-vm qrexec-client-vm.o | ||||||
| clean: | clean: | ||||||
| @ -12,9 +14,10 @@ clean: | |||||||
| 
 | 
 | ||||||
| install: | install: | ||||||
| 	install -d $(DESTDIR)/etc/qubes-rpc | 	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-agent $(DESTDIR)/usr/lib/qubes | ||||||
| 	install qrexec-client-vm $(DESTDIR)/usr/lib/qubes | 	install qrexec-client-vm $(DESTDIR)/usr/lib/qubes | ||||||
| 	ln -s qrexec-client-vm $(DESTDIR)/usr/lib/qubes/qrexec_client_vm | 	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 | 	install qubes-rpc-multiplexer $(DESTDIR)/usr/lib/qubes | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -29,6 +29,7 @@ | |||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
| #include <sys/wait.h> | #include <sys/wait.h> | ||||||
| #include <sys/select.h> | #include <sys/select.h> | ||||||
|  | #include <sys/socket.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <libvchan.h> | #include <libvchan.h> | ||||||
| #include "qrexec.h" | #include "qrexec.h" | ||||||
| @ -38,6 +39,7 @@ | |||||||
| #define VCHAN_BUFFER_SIZE 65536 | #define VCHAN_BUFFER_SIZE 65536 | ||||||
| 
 | 
 | ||||||
| static volatile int child_exited; | static volatile int child_exited; | ||||||
|  | static volatile int stdio_socket_requested; | ||||||
| int stdout_msg_type = MSG_DATA_STDOUT; | int stdout_msg_type = MSG_DATA_STDOUT; | ||||||
| pid_t child_process_pid; | pid_t child_process_pid; | ||||||
| 
 | 
 | ||||||
| @ -47,40 +49,58 @@ static void sigchld_handler(int __attribute__((__unused__))x) | |||||||
|     signal(SIGCHLD, sigchld_handler); |     signal(SIGCHLD, sigchld_handler); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | static void sigusr1_handler(int __attribute__((__unused__))x) | ||||||
| void no_colon_in_cmd() |  | ||||||
| { | { | ||||||
| 	fprintf(stderr, |     stdio_socket_requested = 1; | ||||||
| 		"cmdline is supposed to be in user:command form\n"); |     signal(SIGUSR1, SIG_IGN); | ||||||
| 	exit(1); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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]; |     struct msg_header hdr; | ||||||
| 	char *realcmd = index(cmd, ':'); |     struct peer_info info; | ||||||
| 	if (!realcmd) | 
 | ||||||
| 		no_colon_in_cmd(); |     /* send own HELLO */ | ||||||
| 	/* mark end of username and move to command */ |     hdr.type = MSG_HELLO; | ||||||
| 	*realcmd = 0; |     hdr.len = sizeof(info); | ||||||
| 	realcmd++; |     info.version = QREXEC_PROTOCOL_VERSION; | ||||||
| 	/* ignore "nogui:" prefix in linux agent */ | 
 | ||||||
| 	if (strncmp(realcmd, "nogui:", 6) == 0) |     if (libvchan_send(ctrl, &hdr, sizeof(hdr)) != sizeof(hdr)) { | ||||||
| 		realcmd+=6; |         fprintf(stderr, "Failed to send HELLO hdr to agent\n"); | ||||||
| 	/* replace magic RPC cmd with RPC multiplexer path */ |         return -1; | ||||||
| 	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); |     if (libvchan_send(ctrl, &info, sizeof(info)) != sizeof(info)) { | ||||||
| 	perror("execl"); |         fprintf(stderr, "Failed to send HELLO hdr to agent\n"); | ||||||
| 	exit(1); |         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 handle_just_exec(char *cmdline) | ||||||
| { | { | ||||||
|     int fdn, pid; |     int fdn, pid; | ||||||
| @ -146,7 +166,10 @@ int handle_input(libvchan_t *vchan, int fd, int msg_type) | |||||||
|             return -1; |             return -1; | ||||||
| 
 | 
 | ||||||
|         if (len == 0) { |         if (len == 0) { | ||||||
|  |             if (shutdown(fd, SHUT_RD) < 0) { | ||||||
|  |                 if (errno == ENOTSOCK) | ||||||
|                     close(fd); |                     close(fd); | ||||||
|  |             } | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -188,14 +211,20 @@ int handle_remote_data(libvchan_t *data_vchan, int stdin_fd) | |||||||
|                     /* discard the data */ |                     /* discard the data */ | ||||||
|                     continue; |                     continue; | ||||||
|                 if (hdr.len == 0) { |                 if (hdr.len == 0) { | ||||||
|  |                     if (shutdown(stdin_fd, SHUT_WR) < 0) { | ||||||
|  |                         if (errno == ENOTSOCK) | ||||||
|                             close(stdin_fd); |                             close(stdin_fd); | ||||||
|  |                     } | ||||||
|                     stdin_fd = -1; |                     stdin_fd = -1; | ||||||
|                     return 0; |                     return 0; | ||||||
|                 } else { |                 } else { | ||||||
|                     /* FIXME: use buffered write here to prevent deadlock */ |                     /* FIXME: use buffered write here to prevent deadlock */ | ||||||
|                     if (!write_all(stdin_fd, buf, hdr.len)) { |                     if (!write_all(stdin_fd, buf, hdr.len)) { | ||||||
|                         if (errno == EPIPE) { |                         if (errno == EPIPE || errno == ECONNRESET) { | ||||||
|  |                             if (shutdown(stdin_fd, SHUT_WR) < 0) { | ||||||
|  |                                 if (errno == ENOTSOCK) | ||||||
|                                     close(stdin_fd); |                                     close(stdin_fd); | ||||||
|  |                             } | ||||||
|                             stdin_fd = -1; |                             stdin_fd = -1; | ||||||
|                         } else { |                         } else { | ||||||
|                             perror("write"); |                             perror("write"); | ||||||
| @ -248,7 +277,10 @@ void process_child_io(libvchan_t *data_vchan, | |||||||
|                 if (pid == child_process_pid) { |                 if (pid == child_process_pid) { | ||||||
|                     child_process_status = WEXITSTATUS(status); |                     child_process_status = WEXITSTATUS(status); | ||||||
|                     if (stdin_fd >= 0) { |                     if (stdin_fd >= 0) { | ||||||
|  |                         if (shutdown(stdin_fd, SHUT_WR) < 0) { | ||||||
|  |                             if (errno == ENOTSOCK) | ||||||
|                                 close(stdin_fd); |                                 close(stdin_fd); | ||||||
|  |                         } | ||||||
|                         stdin_fd = -1; |                         stdin_fd = -1; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @ -264,6 +296,13 @@ void process_child_io(libvchan_t *data_vchan, | |||||||
|             } |             } | ||||||
|             break; |             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 */ |         /* otherwise handle the events */ | ||||||
| 
 | 
 | ||||||
|         FD_ZERO(&rdset); |         FD_ZERO(&rdset); | ||||||
| @ -337,7 +376,10 @@ void process_child_io(libvchan_t *data_vchan, | |||||||
|                 break; |                 break; | ||||||
|             case -2: |             case -2: | ||||||
|                 /* remote process exited, no sense in sending more data to it */ |                 /* remote process exited, no sense in sending more data to it */ | ||||||
|  |                 if (shutdown(stdout_fd, SHUT_RD) < 0) { | ||||||
|  |                     if (errno == ENOTSOCK) | ||||||
|                         close(stdout_fd); |                         close(stdout_fd); | ||||||
|  |                 } | ||||||
|                 stdout_fd = -1; |                 stdout_fd = -1; | ||||||
|                 close(stderr_fd); |                 close(stderr_fd); | ||||||
|                 stderr_fd = -1; |                 stderr_fd = -1; | ||||||
| @ -353,6 +395,7 @@ pid_t handle_new_process(int type, int connect_domain, int connect_port, | |||||||
|     libvchan_t *data_vchan; |     libvchan_t *data_vchan; | ||||||
|     pid_t pid; |     pid_t pid; | ||||||
|     int stdin_fd, stdout_fd, stderr_fd; |     int stdin_fd, stdout_fd, stderr_fd; | ||||||
|  |     char pid_s[10]; | ||||||
| 
 | 
 | ||||||
|     if (type == MSG_SERVICE_CONNECT) { |     if (type == MSG_SERVICE_CONNECT) { | ||||||
|         if (cmdline_len != sizeof(*svc_params)) { |         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); |     handle_handshake(data_vchan); | ||||||
| 
 | 
 | ||||||
|     signal(SIGCHLD, sigchld_handler); |     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) { |     switch (type) { | ||||||
|         case MSG_JUST_EXEC: |         case MSG_JUST_EXEC: | ||||||
|  | |||||||
| @ -19,7 +19,10 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #define _GNU_SOURCE | ||||||
| #include <sys/select.h> | #include <sys/select.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <sys/un.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <signal.h> | #include <signal.h> | ||||||
| @ -38,7 +41,8 @@ | |||||||
| #include "qrexec-agent.h" | #include "qrexec-agent.h" | ||||||
| 
 | 
 | ||||||
| struct _connection_info { | 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_domain; | ||||||
|     int connect_port; |     int connect_port; | ||||||
| }; | }; | ||||||
| @ -55,7 +59,38 @@ int passfd_socket; | |||||||
| 
 | 
 | ||||||
| int meminfo_write_started = 0; | 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) | void handle_vchan_error(const char *op) | ||||||
| { | { | ||||||
| @ -63,51 +98,6 @@ void handle_vchan_error(const char *op) | |||||||
|     exit(1); |     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() | void init() | ||||||
| { | { | ||||||
|     /* FIXME: This 0 is remote domain ID */ |     /* FIXME: This 0 is remote domain ID */ | ||||||
| @ -161,13 +151,70 @@ void wake_meminfo_writer() | |||||||
|     meminfo_write_started = 1; |     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; |     int i; | ||||||
| 
 | 
 | ||||||
|     for (i = 0; i < MAX_FDS; i++) { |     for (i = 0; i < MAX_FDS; i++) { | ||||||
|         if (connection_info[i].pid == 0) { |         if (connection_info[i].pid == 0) { | ||||||
|             connection_info[i].pid = pid; |             connection_info[i].pid = pid; | ||||||
|  |             connection_info[i].fd = fd; | ||||||
|             connection_info[i].connect_domain = domain; |             connection_info[i].connect_domain = domain; | ||||||
|             connection_info[i].connect_port = port; |             connection_info[i].connect_port = port; | ||||||
|             return; |             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) |     if (libvchan_recv(ctrl_vchan, buf, hdr->len-sizeof(params)) < 0) | ||||||
|         handle_vchan_error("read exec cmd"); |         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, |     child_agent = handle_new_process(hdr->type, | ||||||
|             params.connect_domain, params.connect_port, |             params.connect_domain, params.connect_port, | ||||||
|             buf, hdr->len-sizeof(params)); |             buf, hdr->len-sizeof(params)); | ||||||
| 
 | 
 | ||||||
|     register_vchan_connection(child_agent, |     register_vchan_connection(child_agent, -1, | ||||||
|             params.connect_domain, params.connect_port); |             params.connect_domain, params.connect_port); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -262,18 +321,10 @@ int find_connection(int pid) | |||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | void release_connection(int id) { | ||||||
| void reap_children() |  | ||||||
| { |  | ||||||
|     int status; |  | ||||||
|     int pid; |  | ||||||
|     int id; |  | ||||||
|     struct msg_header hdr; |     struct msg_header hdr; | ||||||
|     struct exec_params params; |     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.type = MSG_CONNECTION_TERMINATED; | ||||||
|     hdr.len = sizeof(struct exec_params); |     hdr.len = sizeof(struct exec_params); | ||||||
|     params.connect_domain = connection_info[id].connect_domain; |     params.connect_domain = connection_info[id].connect_domain; | ||||||
| @ -283,6 +334,18 @@ void reap_children() | |||||||
|     if (libvchan_send(ctrl_vchan, ¶ms, sizeof(params)) < 0) |     if (libvchan_send(ctrl_vchan, ¶ms, sizeof(params)) < 0) | ||||||
|         handle_vchan_error("send"); |         handle_vchan_error("send"); | ||||||
|     connection_info[id].pid = 0; |     connection_info[id].pid = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void reap_children() | ||||||
|  | { | ||||||
|  |     int status; | ||||||
|  |     int pid; | ||||||
|  |     int id; | ||||||
|  |     while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { | ||||||
|  |         id = find_connection(pid); | ||||||
|  |         if (id < 0) | ||||||
|  |             continue; | ||||||
|  |         release_connection(id); | ||||||
|     } |     } | ||||||
|     child_exited = 0; |     child_exited = 0; | ||||||
| } | } | ||||||
| @ -290,6 +353,7 @@ void reap_children() | |||||||
| int fill_fds_for_select(fd_set * rdset, fd_set * wrset) | int fill_fds_for_select(fd_set * rdset, fd_set * wrset) | ||||||
| { | { | ||||||
|     int max = -1; |     int max = -1; | ||||||
|  |     int i; | ||||||
|     FD_ZERO(rdset); |     FD_ZERO(rdset); | ||||||
|     FD_ZERO(wrset); |     FD_ZERO(wrset); | ||||||
| 
 | 
 | ||||||
| @ -299,6 +363,14 @@ int fill_fds_for_select(fd_set * rdset, fd_set * wrset) | |||||||
|     FD_SET(passfd_socket, rdset); |     FD_SET(passfd_socket, rdset); | ||||||
|     if (passfd_socket > max) |     if (passfd_socket > max) | ||||||
|         max = passfd_socket; |         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; |     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() | int main() | ||||||
| { | { | ||||||
|     fd_set rdset, wrset; |     fd_set rdset, wrset; | ||||||
| @ -372,5 +462,7 @@ int main() | |||||||
| 
 | 
 | ||||||
|         if (FD_ISSET(trigger_fd, &rdset)) |         if (FD_ISSET(trigger_fd, &rdset)) | ||||||
|             handle_trigger_io(); |             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); | int handle_handshake(libvchan_t *ctrl); | ||||||
| void handle_vchan_error(const char *op); | void handle_vchan_error(const char *op); | ||||||
|  | void do_exec(const char *cmd); | ||||||
| 
 | 
 | ||||||
| pid_t handle_new_process(int type, | pid_t handle_new_process(int type, | ||||||
|         int connect_domain, int connect_port, |         int connect_domain, int connect_port, | ||||||
|         char *cmdline, int cmdline_len); |         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/qvm-mru-entry | ||||||
| /usr/bin/xenstore-watch-qubes | /usr/bin/xenstore-watch-qubes | ||||||
| /usr/bin/qubes-desktop-run | /usr/bin/qubes-desktop-run | ||||||
|  | /usr/bin/qrexec-fork-server | ||||||
| %dir /usr/lib/qubes | %dir /usr/lib/qubes | ||||||
| /usr/lib/qubes/vusb-ctl.py* | /usr/lib/qubes/vusb-ctl.py* | ||||||
| /usr/lib/qubes/dispvm-prerun.sh | /usr/lib/qubes/dispvm-prerun.sh | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Marek Marczykowski-Górecki
						Marek Marczykowski-Górecki