diff --git a/qrexec/qrexec-agent.c b/qrexec/qrexec-agent.c index faa4b7c..2e83a7d 100644 --- a/qrexec/qrexec-agent.c +++ b/qrexec/qrexec-agent.c @@ -36,27 +36,27 @@ #include "libqrexec-utils.h" enum fdtype { - FDTYPE_INVALID, - FDTYPE_STDOUT, - FDTYPE_STDERR + FDTYPE_INVALID, + FDTYPE_STDOUT, + FDTYPE_STDERR }; struct _process_fd { - int client_id; - int type; - int is_blocked; + int client_id; + int type; + int is_blocked; }; struct _client_info { - int stdin_fd; - int stdout_fd; - int stderr_fd; + int stdin_fd; + int stdout_fd; + int stderr_fd; - int exit_status; - int is_exited; - int pid; - int is_blocked; - int is_close_after_flush_needed; - struct buffer buffer; + int exit_status; + int is_exited; + int pid; + int is_blocked; + int is_close_after_flush_needed; + struct buffer buffer; }; int max_process_fd = -1; @@ -76,557 +76,557 @@ int meminfo_write_started = 0; void do_exec(const char *cmd); void handle_vchan_error(const char *op) { - fprintf(stderr, "Error while vchan %s, exiting\n", op); - exit(1); + fprintf(stderr, "Error while vchan %s, exiting\n", op); + exit(1); } void init() { - /* FIXME: This 0 is remote domain ID */ - vchan = libvchan_server_init(0, REXEC_PORT, 4096, 4096); - if (!vchan) - handle_vchan_error("server_init"); - umask(0); - mkfifo(QREXEC_AGENT_TRIGGER_PATH, 0666); - passfd_socket = get_server_socket(QREXEC_AGENT_FDPASS_PATH); - umask(077); - trigger_fd = - open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK); - register_exec_func(do_exec); + /* FIXME: This 0 is remote domain ID */ + vchan = libvchan_server_init(0, REXEC_PORT, 4096, 4096); + if (!vchan) + handle_vchan_error("server_init"); + umask(0); + mkfifo(QREXEC_AGENT_TRIGGER_PATH, 0666); + passfd_socket = get_server_socket(QREXEC_AGENT_FDPASS_PATH); + umask(077); + trigger_fd = + open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK); + register_exec_func(do_exec); - /* wait for qrexec daemon */ - while (!libvchan_is_open(vchan)) - libvchan_wait(vchan); + /* wait for qrexec daemon */ + while (!libvchan_is_open(vchan)) + libvchan_wait(vchan); } void wake_meminfo_writer() { - FILE *f; - int pid; + FILE *f; + int pid; - if (meminfo_write_started) - /* wake meminfo-writer only once */ - return; + if (meminfo_write_started) + /* wake meminfo-writer only once */ + return; - f = fopen(MEMINFO_WRITER_PIDFILE, "r"); - if (f == NULL) { - /* no meminfo-writer found, ignoring */ - return; - } - if (fscanf(f, "%d", &pid) < 1) { - fclose(f); - /* no meminfo-writer found, ignoring */ - return; - } + f = fopen(MEMINFO_WRITER_PIDFILE, "r"); + if (f == NULL) { + /* no meminfo-writer found, ignoring */ + return; + } + if (fscanf(f, "%d", &pid) < 1) { + fclose(f); + /* no meminfo-writer found, ignoring */ + return; + } - fclose(f); - if (pid <= 1 || pid > 0xffff) { - /* check within acceptable range */ - return; - } - if (kill(pid, SIGUSR1) < 0) { - /* Can't send signal */ - return; - } - meminfo_write_started = 1; + fclose(f); + if (pid <= 1 || pid > 0xffff) { + /* check within acceptable range */ + return; + } + if (kill(pid, SIGUSR1) < 0) { + /* Can't send signal */ + return; + } + meminfo_write_started = 1; } void no_colon_in_cmd() { - fprintf(stderr, - "cmdline is supposed to be in user:command form\n"); - exit(1); + 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(QUBES_RPC_MAGIC_CMD) + 1]; - char *realcmd = index(cmd, ':'), *user; - if (!realcmd) - no_colon_in_cmd(); - /* mark end of username and move to command */ - user=strndup(cmd,realcmd-cmd); - 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, QUBES_RPC_MAGIC_CMD " ", strlen(QUBES_RPC_MAGIC_CMD)+1)==0) { - strcpy(buf, QUBES_RPC_MULTIPLEXER_PATH); - strcpy(buf + strlen(QUBES_RPC_MULTIPLEXER_PATH), realcmd + strlen(QUBES_RPC_MAGIC_CMD)); - realcmd = buf; - } - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); + char buf[strlen(QUBES_RPC_MULTIPLEXER_PATH) + strlen(cmd) - strlen(QUBES_RPC_MAGIC_CMD) + 1]; + char *realcmd = index(cmd, ':'), *user; + if (!realcmd) + no_colon_in_cmd(); + /* mark end of username and move to command */ + user=strndup(cmd,realcmd-cmd); + 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, QUBES_RPC_MAGIC_CMD " ", strlen(QUBES_RPC_MAGIC_CMD)+1)==0) { + strcpy(buf, QUBES_RPC_MULTIPLEXER_PATH); + strcpy(buf + strlen(QUBES_RPC_MULTIPLEXER_PATH), realcmd + strlen(QUBES_RPC_MAGIC_CMD)); + realcmd = buf; + } + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); - execl("/bin/su", "su", "-", user, "-c", realcmd, NULL); - perror("execl"); - exit(1); + execl("/bin/su", "su", "-", user, "-c", realcmd, NULL); + perror("execl"); + exit(1); } void handle_just_exec(int len) { - char buf[len]; - int fdn, pid; + char buf[len]; + int fdn, pid; - if (libvchan_recv(vchan, buf, len) < 0) - handle_vchan_error("read"); - switch (pid = fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - fdn = open("/dev/null", O_RDWR); - fix_fds(fdn, fdn, fdn); - do_exec(buf); - perror("execl"); - exit(1); - default:; - } - fprintf(stderr, "executed (nowait) %s pid %d\n", buf, pid); + if (libvchan_recv(vchan, buf, len) < 0) + handle_vchan_error("read"); + switch (pid = fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + fdn = open("/dev/null", O_RDWR); + fix_fds(fdn, fdn, fdn); + do_exec(buf); + perror("execl"); + exit(1); + default:; + } + fprintf(stderr, "executed (nowait) %s pid %d\n", buf, pid); } void create_info_about_client(int client_id, int pid, int stdin_fd, - int stdout_fd, int stderr_fd) + int stdout_fd, int stderr_fd) { - process_fd[stdout_fd].client_id = client_id; - process_fd[stdout_fd].type = FDTYPE_STDOUT; - process_fd[stdout_fd].is_blocked = 0; - process_fd[stderr_fd].client_id = client_id; - process_fd[stderr_fd].type = FDTYPE_STDERR; - process_fd[stderr_fd].is_blocked = 0; + process_fd[stdout_fd].client_id = client_id; + process_fd[stdout_fd].type = FDTYPE_STDOUT; + process_fd[stdout_fd].is_blocked = 0; + process_fd[stderr_fd].client_id = client_id; + process_fd[stderr_fd].type = FDTYPE_STDERR; + process_fd[stderr_fd].is_blocked = 0; - if (stderr_fd > max_process_fd) - max_process_fd = stderr_fd; - if (stdout_fd > max_process_fd) - max_process_fd = stdout_fd; + if (stderr_fd > max_process_fd) + max_process_fd = stderr_fd; + if (stdout_fd > max_process_fd) + max_process_fd = stdout_fd; - set_nonblock(stdin_fd); + set_nonblock(stdin_fd); - client_info[client_id].stdin_fd = stdin_fd; - client_info[client_id].stdout_fd = stdout_fd; - client_info[client_id].stderr_fd = stderr_fd; - client_info[client_id].exit_status = 0; - client_info[client_id].is_exited = 0; - client_info[client_id].pid = pid; - client_info[client_id].is_blocked = 0; - client_info[client_id].is_close_after_flush_needed = 0; - buffer_init(&client_info[client_id].buffer); + client_info[client_id].stdin_fd = stdin_fd; + client_info[client_id].stdout_fd = stdout_fd; + client_info[client_id].stderr_fd = stderr_fd; + client_info[client_id].exit_status = 0; + client_info[client_id].is_exited = 0; + client_info[client_id].pid = pid; + client_info[client_id].is_blocked = 0; + client_info[client_id].is_close_after_flush_needed = 0; + 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; + char buf[len]; + int pid, stdin_fd, stdout_fd, stderr_fd; - if (libvchan_recv(vchan, buf, len) < 0) - handle_vchan_error("read"); + if (libvchan_recv(vchan, buf, len) < 0) + handle_vchan_error("read"); - do_fork_exec(buf, &pid, &stdin_fd, &stdout_fd, &stderr_fd); + do_fork_exec(buf, &pid, &stdin_fd, &stdout_fd, &stderr_fd); - create_info_about_client(client_id, 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 len) { - int stdin_fd, stdout_fd, stderr_fd; - char buf[len]; - if (libvchan_recv(vchan, buf, len) < 0) - handle_vchan_error("read"); - sscanf(buf, "%d %d %d", &stdin_fd, &stdout_fd, &stderr_fd); - create_info_about_client(client_id, -1, stdin_fd, stdout_fd, - stderr_fd); - client_info[client_id].is_exited = 1; //do not wait for SIGCHLD + int stdin_fd, stdout_fd, stderr_fd; + char buf[len]; + if (libvchan_recv(vchan, buf, len) < 0) + handle_vchan_error("read"); + sscanf(buf, "%d %d %d", &stdin_fd, &stdout_fd, &stderr_fd); + create_info_about_client(client_id, -1, stdin_fd, stdout_fd, + stderr_fd); + client_info[client_id].is_exited = 1; //do not wait for SIGCHLD } void update_max_process_fd() { - int i; - for (i = max_process_fd; - i >= 0 && process_fd[i].type == FDTYPE_INVALID; i--); - max_process_fd = i; + int i; + for (i = max_process_fd; + i >= 0 && process_fd[i].type == FDTYPE_INVALID; i--); + max_process_fd = i; } void send_exit_code(int client_id, int status) { - struct server_header s_hdr; - s_hdr.type = MSG_AGENT_TO_SERVER_EXIT_CODE; - s_hdr.client_id = client_id; - s_hdr.len = sizeof status; - if (libvchan_send(vchan, &s_hdr, sizeof(s_hdr)) < 0) - handle_vchan_error("write hdr"); - if (libvchan_send(vchan, &status, sizeof(status)) < 0) - handle_vchan_error("write status"); - fprintf(stderr, "send exit code %d for client_id %d pid %d\n", - status, client_id, client_info[client_id].pid); + struct server_header s_hdr; + s_hdr.type = MSG_AGENT_TO_SERVER_EXIT_CODE; + s_hdr.client_id = client_id; + s_hdr.len = sizeof status; + if (libvchan_send(vchan, &s_hdr, sizeof(s_hdr)) < 0) + handle_vchan_error("write hdr"); + if (libvchan_send(vchan, &status, sizeof(status)) < 0) + handle_vchan_error("write status"); + fprintf(stderr, "send exit code %d for client_id %d pid %d\n", + status, client_id, client_info[client_id].pid); } // erase process data structures, possibly forced by remote void remove_process(int client_id, int status) { - int i; - if (!client_info[client_id].pid) - return; - if (client_info[client_id].stdin_fd >= 0) - fork_and_flush_stdin(client_info[client_id].stdin_fd, - &client_info[client_id].buffer); + int i; + if (!client_info[client_id].pid) + return; + if (client_info[client_id].stdin_fd >= 0) + fork_and_flush_stdin(client_info[client_id].stdin_fd, + &client_info[client_id].buffer); #if 0 -// let's let it die by itself, possibly after it has received buffered stdin - kill(client_info[client_id].pid, SIGKILL); + // let's let it die by itself, possibly after it has received buffered stdin + kill(client_info[client_id].pid, SIGKILL); #endif - if (status != -1) - send_exit_code(client_id, status); + if (status != -1) + send_exit_code(client_id, status); - close(client_info[client_id].stdin_fd); - client_info[client_id].pid = 0; - client_info[client_id].stdin_fd = -1; - client_info[client_id].is_blocked = 0; - buffer_free(&client_info[client_id].buffer); + close(client_info[client_id].stdin_fd); + client_info[client_id].pid = 0; + client_info[client_id].stdin_fd = -1; + client_info[client_id].is_blocked = 0; + buffer_free(&client_info[client_id].buffer); - for (i = 0; i <= max_process_fd; i++) - if (process_fd[i].type != FDTYPE_INVALID - && process_fd[i].client_id == client_id) { - process_fd[i].type = FDTYPE_INVALID; - process_fd[i].client_id = -1; - process_fd[i].is_blocked = 0; - close(i); - } - update_max_process_fd(); + for (i = 0; i <= max_process_fd; i++) + if (process_fd[i].type != FDTYPE_INVALID + && process_fd[i].client_id == client_id) { + process_fd[i].type = FDTYPE_INVALID; + process_fd[i].client_id = -1; + process_fd[i].is_blocked = 0; + close(i); + } + update_max_process_fd(); } // remove process not immediately after it has exited, but after its stdout and stderr has been drained // previous method implemented in flush_out_err was broken - it cannot work when peer signalled it is blocked void possibly_remove_process(int client_id) { - if (client_info[client_id].stdout_fd == -1 && - client_info[client_id].stderr_fd == -1 && - client_info[client_id].is_exited) - remove_process(client_id, - client_info[client_id].exit_status); + if (client_info[client_id].stdout_fd == -1 && + client_info[client_id].stderr_fd == -1 && + client_info[client_id].is_exited) + remove_process(client_id, + client_info[client_id].exit_status); } void handle_input(int client_id, int len) { - char buf[len]; + char buf[len]; - if (libvchan_recv(vchan, buf, len) < 0) - handle_vchan_error("read"); - if (!client_info[client_id].pid || client_info[client_id].stdin_fd == -1) - return; + if (libvchan_recv(vchan, buf, len) < 0) + handle_vchan_error("read"); + if (!client_info[client_id].pid || client_info[client_id].stdin_fd == -1) + return; - if (len == 0) { - if (client_info[client_id].is_blocked) - client_info[client_id].is_close_after_flush_needed - = 1; - else { - close(client_info[client_id].stdin_fd); - client_info[client_id].stdin_fd = -1; - } - return; - } + if (len == 0) { + if (client_info[client_id].is_blocked) + client_info[client_id].is_close_after_flush_needed + = 1; + else { + close(client_info[client_id].stdin_fd); + client_info[client_id].stdin_fd = -1; + } + return; + } - switch (write_stdin - (vchan, client_info[client_id].stdin_fd, client_id, buf, len, - &client_info[client_id].buffer)) { - case WRITE_STDIN_OK: - break; - case WRITE_STDIN_BUFFERED: - client_info[client_id].is_blocked = 1; - break; - case WRITE_STDIN_ERROR: - // do not remove process, as it still can write data to stdout - close(client_info[client_id].stdin_fd); - client_info[client_id].stdin_fd = -1; - client_info[client_id].is_blocked = 0; - break; - default: - fprintf(stderr, "unknown write_stdin?\n"); - exit(1); - } + switch (write_stdin + (vchan, client_info[client_id].stdin_fd, client_id, buf, len, + &client_info[client_id].buffer)) { + case WRITE_STDIN_OK: + break; + case WRITE_STDIN_BUFFERED: + client_info[client_id].is_blocked = 1; + break; + case WRITE_STDIN_ERROR: + // do not remove process, as it still can write data to stdout + close(client_info[client_id].stdin_fd); + client_info[client_id].stdin_fd = -1; + client_info[client_id].is_blocked = 0; + break; + default: + fprintf(stderr, "unknown write_stdin?\n"); + exit(1); + } } void set_blocked_outerr(int client_id, int val) { - process_fd[client_info[client_id].stdout_fd].is_blocked = val; - process_fd[client_info[client_id].stderr_fd].is_blocked = val; + process_fd[client_info[client_id].stdout_fd].is_blocked = val; + process_fd[client_info[client_id].stderr_fd].is_blocked = val; } void handle_server_data() { - struct server_header s_hdr; - if (libvchan_recv(vchan, &s_hdr, sizeof(s_hdr)) < 0) - handle_vchan_error("read s_hdr"); + struct server_header s_hdr; + if (libvchan_recv(vchan, &s_hdr, sizeof(s_hdr)) < 0) + handle_vchan_error("read s_hdr"); -// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.client_id, -// s_hdr.len); + // fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.client_id, + // s_hdr.len); - switch (s_hdr.type) { - case MSG_XON: - set_blocked_outerr(s_hdr.client_id, 0); - break; - case MSG_XOFF: - set_blocked_outerr(s_hdr.client_id, 1); - break; - case MSG_SERVER_TO_AGENT_CONNECT_EXISTING: - handle_connect_existing(s_hdr.client_id, s_hdr.len); - break; - case MSG_SERVER_TO_AGENT_EXEC_CMDLINE: - wake_meminfo_writer(); - handle_exec(s_hdr.client_id, s_hdr.len); - break; - case MSG_SERVER_TO_AGENT_JUST_EXEC: - wake_meminfo_writer(); - handle_just_exec(s_hdr.len); - break; - case MSG_SERVER_TO_AGENT_INPUT: - handle_input(s_hdr.client_id, s_hdr.len); - break; - case MSG_SERVER_TO_AGENT_CLIENT_END: - remove_process(s_hdr.client_id, -1); - break; - default: - fprintf(stderr, "msg type from daemon is %d ?\n", - s_hdr.type); - exit(1); - } + switch (s_hdr.type) { + case MSG_XON: + set_blocked_outerr(s_hdr.client_id, 0); + break; + case MSG_XOFF: + set_blocked_outerr(s_hdr.client_id, 1); + break; + case MSG_SERVER_TO_AGENT_CONNECT_EXISTING: + handle_connect_existing(s_hdr.client_id, s_hdr.len); + break; + case MSG_SERVER_TO_AGENT_EXEC_CMDLINE: + wake_meminfo_writer(); + handle_exec(s_hdr.client_id, s_hdr.len); + break; + case MSG_SERVER_TO_AGENT_JUST_EXEC: + wake_meminfo_writer(); + handle_just_exec(s_hdr.len); + break; + case MSG_SERVER_TO_AGENT_INPUT: + handle_input(s_hdr.client_id, s_hdr.len); + break; + case MSG_SERVER_TO_AGENT_CLIENT_END: + remove_process(s_hdr.client_id, -1); + break; + default: + fprintf(stderr, "msg type from daemon is %d ?\n", + s_hdr.type); + exit(1); + } } void handle_process_data(int fd) { - struct server_header s_hdr; - char buf[MAX_DATA_CHUNK]; - int ret; - unsigned int len; + struct server_header s_hdr; + char buf[MAX_DATA_CHUNK]; + int ret; + unsigned int len; - len = libvchan_buffer_space(vchan); - if (len <= sizeof s_hdr) - return; + len = libvchan_buffer_space(vchan); + if (len <= sizeof s_hdr) + return; - ret = read(fd, buf, len - sizeof s_hdr); - s_hdr.client_id = process_fd[fd].client_id; + ret = read(fd, buf, len - sizeof s_hdr); + s_hdr.client_id = process_fd[fd].client_id; - if (process_fd[fd].type == FDTYPE_STDOUT) - s_hdr.type = MSG_AGENT_TO_SERVER_STDOUT; - else if (process_fd[fd].type == FDTYPE_STDERR) - s_hdr.type = MSG_AGENT_TO_SERVER_STDERR; - else { - fprintf(stderr, "fd=%d, client_id=%d, type=%d ?\n", fd, - process_fd[fd].client_id, process_fd[fd].type); - exit(1); - } - s_hdr.len = ret; - if (ret >= 0) { - if (libvchan_send(vchan, &s_hdr, sizeof(s_hdr)) < 0) - handle_vchan_error("write hdr"); - if (libvchan_send(vchan, buf, ret) < 0) - handle_vchan_error("write buf"); - } - if (ret == 0) { - int client_id = process_fd[fd].client_id; - if (process_fd[fd].type == FDTYPE_STDOUT) - client_info[client_id].stdout_fd = -1; - else - client_info[client_id].stderr_fd = -1; + if (process_fd[fd].type == FDTYPE_STDOUT) + s_hdr.type = MSG_AGENT_TO_SERVER_STDOUT; + else if (process_fd[fd].type == FDTYPE_STDERR) + s_hdr.type = MSG_AGENT_TO_SERVER_STDERR; + else { + fprintf(stderr, "fd=%d, client_id=%d, type=%d ?\n", fd, + process_fd[fd].client_id, process_fd[fd].type); + exit(1); + } + s_hdr.len = ret; + if (ret >= 0) { + if (libvchan_send(vchan, &s_hdr, sizeof(s_hdr)) < 0) + handle_vchan_error("write hdr"); + if (libvchan_send(vchan, buf, ret) < 0) + handle_vchan_error("write buf"); + } + if (ret == 0) { + int client_id = process_fd[fd].client_id; + if (process_fd[fd].type == FDTYPE_STDOUT) + client_info[client_id].stdout_fd = -1; + else + client_info[client_id].stderr_fd = -1; - process_fd[fd].type = FDTYPE_INVALID; - process_fd[fd].client_id = -1; - process_fd[fd].is_blocked = 0; - close(fd); - update_max_process_fd(); - possibly_remove_process(client_id); - } - if (ret < 0) - remove_process(process_fd[fd].client_id, 127); + process_fd[fd].type = FDTYPE_INVALID; + process_fd[fd].client_id = -1; + process_fd[fd].is_blocked = 0; + close(fd); + update_max_process_fd(); + possibly_remove_process(client_id); + } + if (ret < 0) + remove_process(process_fd[fd].client_id, 127); } volatile int child_exited; void sigchld_handler(int x __attribute__((__unused__))) { - child_exited = 1; - signal(SIGCHLD, sigchld_handler); + child_exited = 1; + signal(SIGCHLD, sigchld_handler); } int find_info(int pid) { - int i; - for (i = 0; i < MAX_FDS; i++) - if (client_info[i].pid == pid) - return i; - return -1; + int i; + for (i = 0; i < MAX_FDS; i++) + if (client_info[i].pid == pid) + return i; + return -1; } void handle_process_data_all(fd_set * select_fds) { - int i; - for (i = 0; i <= max_process_fd; i++) - if (process_fd[i].type != FDTYPE_INVALID - && FD_ISSET(i, select_fds)) - handle_process_data(i); + int i; + for (i = 0; i <= max_process_fd; i++) + if (process_fd[i].type != FDTYPE_INVALID + && FD_ISSET(i, select_fds)) + handle_process_data(i); } void reap_children() { - int status; - int pid; - int client_id; - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { - client_id = find_info(pid); - if (client_id < 0) - continue; - client_info[client_id].is_exited = 1; - client_info[client_id].exit_status = status; - possibly_remove_process(client_id); - } - child_exited = 0; + int status; + int pid; + int client_id; + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + client_id = find_info(pid); + if (client_id < 0) + continue; + client_info[client_id].is_exited = 1; + client_info[client_id].exit_status = status; + possibly_remove_process(client_id); + } + child_exited = 0; } int fill_fds_for_select(fd_set * rdset, fd_set * wrset) { - int max = -1; - int fd, i; - FD_ZERO(rdset); - FD_ZERO(wrset); + int max = -1; + int fd, i; + FD_ZERO(rdset); + FD_ZERO(wrset); - for (i = 0; i <= max_process_fd; i++) - if (process_fd[i].type != FDTYPE_INVALID - && !process_fd[i].is_blocked) { - FD_SET(i, rdset); - max = i; - } + for (i = 0; i <= max_process_fd; i++) + if (process_fd[i].type != FDTYPE_INVALID + && !process_fd[i].is_blocked) { + FD_SET(i, rdset); + max = i; + } - FD_SET(trigger_fd, rdset); - if (trigger_fd > max) - max = trigger_fd; - FD_SET(passfd_socket, rdset); - if (passfd_socket > max) - max = passfd_socket; + FD_SET(trigger_fd, rdset); + if (trigger_fd > max) + max = trigger_fd; + FD_SET(passfd_socket, rdset); + if (passfd_socket > max) + max = passfd_socket; - for (i = 0; i < MAX_FDS; i++) - if (client_info[i].pid && client_info[i].is_blocked) { - fd = client_info[i].stdin_fd; - FD_SET(fd, wrset); - if (fd > max) - max = fd; - } - return max; + for (i = 0; i < MAX_FDS; i++) + if (client_info[i].pid && client_info[i].is_blocked) { + fd = client_info[i].stdin_fd; + FD_SET(fd, wrset); + if (fd > max) + max = fd; + } + return max; } void flush_client_data_agent(int client_id) { - struct _client_info *info = &client_info[client_id]; - switch (flush_client_data - (vchan, info->stdin_fd, client_id, &info->buffer)) { - case WRITE_STDIN_OK: - info->is_blocked = 0; - if (info->is_close_after_flush_needed) { - close(info->stdin_fd); - info->stdin_fd = -1; - info->is_close_after_flush_needed = 0; - } - break; - case WRITE_STDIN_ERROR: - // do not remove process, as it still can write data to stdout - info->is_blocked = 0; - close(info->stdin_fd); - info->stdin_fd = -1; - info->is_close_after_flush_needed = 0; - break; - case WRITE_STDIN_BUFFERED: - break; - default: - fprintf(stderr, "unknown flush_client_data?\n"); - exit(1); - } + struct _client_info *info = &client_info[client_id]; + switch (flush_client_data + (vchan, info->stdin_fd, client_id, &info->buffer)) { + case WRITE_STDIN_OK: + info->is_blocked = 0; + if (info->is_close_after_flush_needed) { + close(info->stdin_fd); + info->stdin_fd = -1; + info->is_close_after_flush_needed = 0; + } + break; + case WRITE_STDIN_ERROR: + // do not remove process, as it still can write data to stdout + info->is_blocked = 0; + close(info->stdin_fd); + info->stdin_fd = -1; + info->is_close_after_flush_needed = 0; + break; + case WRITE_STDIN_BUFFERED: + break; + default: + fprintf(stderr, "unknown flush_client_data?\n"); + exit(1); + } } 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 - if (write(fd, &fd, sizeof(fd)) != sizeof(fd)) { - perror("write to client"); - } + 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 + if (write(fd, &fd, sizeof(fd)) != sizeof(fd)) { + perror("write to client"); + } } void handle_trigger_io() { - struct server_header s_hdr; - struct trigger_connect_params params; - int ret; + struct server_header s_hdr; + struct trigger_connect_params params; + int ret; - s_hdr.client_id = 0; - s_hdr.len = 0; - ret = read(trigger_fd, ¶ms, sizeof(params)); - if (ret == sizeof(params)) { - s_hdr.type = MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING; - if (libvchan_send(vchan, &s_hdr, sizeof(s_hdr)) < 0) - handle_vchan_error("write hdr"); - if (libvchan_send(vchan, ¶ms, sizeof(params)) < 0) - handle_vchan_error("write params"); - } -// trigger_fd is nonblock - so no need to reopen -// not really, need to reopen at EOF - if (ret <= 0) { - close(trigger_fd); - trigger_fd = - open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK); - } + s_hdr.client_id = 0; + s_hdr.len = 0; + ret = read(trigger_fd, ¶ms, sizeof(params)); + if (ret == sizeof(params)) { + s_hdr.type = MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING; + if (libvchan_send(vchan, &s_hdr, sizeof(s_hdr)) < 0) + handle_vchan_error("write hdr"); + if (libvchan_send(vchan, ¶ms, sizeof(params)) < 0) + handle_vchan_error("write params"); + } + // trigger_fd is nonblock - so no need to reopen + // not really, need to reopen at EOF + if (ret <= 0) { + close(trigger_fd); + trigger_fd = + open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK); + } } int main() { - fd_set rdset, wrset; - int max; - int i; - sigset_t chld_set; + fd_set rdset, wrset; + int max; + int i; + sigset_t chld_set; - init(); - signal(SIGCHLD, sigchld_handler); - signal(SIGPIPE, SIG_IGN); - sigemptyset(&chld_set); - sigaddset(&chld_set, SIGCHLD); + init(); + signal(SIGCHLD, sigchld_handler); + signal(SIGPIPE, SIG_IGN); + sigemptyset(&chld_set); + sigaddset(&chld_set, SIGCHLD); - for (;;) { - sigprocmask(SIG_BLOCK, &chld_set, NULL); - if (child_exited) - reap_children(); - max = fill_fds_for_select(&rdset, &wrset); - if (libvchan_buffer_space(vchan) <= - sizeof(struct server_header)) - FD_ZERO(&rdset); + for (;;) { + sigprocmask(SIG_BLOCK, &chld_set, NULL); + if (child_exited) + reap_children(); + max = fill_fds_for_select(&rdset, &wrset); + if (libvchan_buffer_space(vchan) <= + sizeof(struct server_header)) + FD_ZERO(&rdset); - wait_for_vchan_or_argfd(vchan, max, &rdset, &wrset); - sigprocmask(SIG_UNBLOCK, &chld_set, NULL); + wait_for_vchan_or_argfd(vchan, max, &rdset, &wrset); + sigprocmask(SIG_UNBLOCK, &chld_set, NULL); - if (FD_ISSET(passfd_socket, &rdset)) - handle_new_passfd(); + if (FD_ISSET(passfd_socket, &rdset)) + handle_new_passfd(); - while (libvchan_data_ready(vchan)) - handle_server_data(); + while (libvchan_data_ready(vchan)) + handle_server_data(); - if (FD_ISSET(trigger_fd, &rdset)) - handle_trigger_io(); + if (FD_ISSET(trigger_fd, &rdset)) + handle_trigger_io(); - handle_process_data_all(&rdset); - for (i = 0; i < MAX_FDS; i++) - if (client_info[i].pid - && client_info[i].is_blocked - && FD_ISSET(client_info[i].stdin_fd, &wrset)) - flush_client_data_agent(i); - } + handle_process_data_all(&rdset); + for (i = 0; i <= MAX_FDS; i++) + if (client_info[i].pid + && client_info[i].is_blocked + && FD_ISSET(client_info[i].stdin_fd, &wrset)) + flush_client_data_agent(i); + } } diff --git a/qrexec/qrexec-client-vm.c b/qrexec/qrexec-client-vm.c index 399f701..aacd4c4 100644 --- a/qrexec/qrexec-client-vm.c +++ b/qrexec/qrexec-client-vm.c @@ -29,92 +29,92 @@ #include "qrexec.h" int connect_unix_socket() { - int s, len; - struct sockaddr_un remote; + int s, len; + struct sockaddr_un remote; - if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - perror("socket"); - return -1; - } + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + perror("socket"); + return -1; + } - remote.sun_family = AF_UNIX; - strncpy(remote.sun_path, QREXEC_AGENT_FDPASS_PATH, - sizeof(remote.sun_path)); - len = strlen(remote.sun_path) + sizeof(remote.sun_family); - if (connect(s, (struct sockaddr *) &remote, len) == -1) { - perror("connect"); - exit(1); - } - return s; + remote.sun_family = AF_UNIX; + strncpy(remote.sun_path, QREXEC_AGENT_FDPASS_PATH, + sizeof(remote.sun_path)); + len = strlen(remote.sun_path) + sizeof(remote.sun_family); + if (connect(s, (struct sockaddr *) &remote, len) == -1) { + perror("connect"); + exit(1); + } + return s; } char *get_program_name(char *prog) { - char *basename = rindex(prog, '/'); - if (basename) - return basename + 1; - else - return prog; + char *basename = rindex(prog, '/'); + if (basename) + return basename + 1; + else + return prog; } int main(int argc, char **argv) { - int trigger_fd; - struct trigger_connect_params params; - int local_fd[3], remote_fd[3]; - int i; - char *abs_exec_path; + int trigger_fd; + struct trigger_connect_params params; + int local_fd[3], remote_fd[3]; + int i; + char *abs_exec_path; - if (argc < 4) { - fprintf(stderr, - "usage: %s target_vmname program_ident local_program [local program arguments]\n", - argv[0]); - exit(1); - } + if (argc < 4) { + fprintf(stderr, + "usage: %s target_vmname program_ident local_program [local program arguments]\n", + argv[0]); + exit(1); + } - trigger_fd = open(QREXEC_AGENT_TRIGGER_PATH, O_WRONLY); - if (trigger_fd < 0) { - perror("open " QREXEC_AGENT_TRIGGER_PATH); - exit(1); - } + trigger_fd = open(QREXEC_AGENT_TRIGGER_PATH, O_WRONLY); + if (trigger_fd < 0) { + perror("open " QREXEC_AGENT_TRIGGER_PATH); + exit(1); + } - for (i = 0; i < 3; i++) { - local_fd[i] = connect_unix_socket(); - if (read(local_fd[i], &remote_fd[i], sizeof(remote_fd[i])) != sizeof(remote_fd[i])) { - perror("read client fd"); - exit(1); - } - if (i != 2 || getenv("PASS_LOCAL_STDERR")) { - char *env; - if (asprintf(&env, "SAVED_FD_%d=%d", i, dup(i)) < 0) { - perror("prepare SAVED_FD_"); - exit(1); - } - putenv(env); - dup2(local_fd[i], i); - close(local_fd[i]); - } - } + for (i = 0; i < 3; i++) { + local_fd[i] = connect_unix_socket(); + if (read(local_fd[i], &remote_fd[i], sizeof(remote_fd[i])) != sizeof(remote_fd[i])) { + perror("read client fd"); + exit(1); + } + if (i != 2 || getenv("PASS_LOCAL_STDERR")) { + char *env; + if (asprintf(&env, "SAVED_FD_%d=%d", i, dup(i)) < 0) { + perror("prepare SAVED_FD_"); + exit(1); + } + putenv(env); + dup2(local_fd[i], i); + close(local_fd[i]); + } + } - memset(¶ms, 0, sizeof(params)); - strncpy(params.exec_index, argv[2], sizeof(params.exec_index)); - strncpy(params.target_vmname, argv[1], - sizeof(params.target_vmname)); - snprintf(params.process_fds.ident, - sizeof(params.process_fds.ident), "%d %d %d", - remote_fd[0], remote_fd[1], remote_fd[2]); + memset(¶ms, 0, sizeof(params)); + strncpy(params.exec_index, argv[2], sizeof(params.exec_index)); + strncpy(params.target_vmname, argv[1], + sizeof(params.target_vmname)); + snprintf(params.process_fds.ident, + sizeof(params.process_fds.ident), "%d %d %d", + remote_fd[0], remote_fd[1], remote_fd[2]); - if (write(trigger_fd, ¶ms, sizeof(params)) < 0) { - if (!getenv("PASS_LOCAL_STDERR")) - perror("write to agent"); - exit(1); - } + if (write(trigger_fd, ¶ms, sizeof(params)) < 0) { + if (!getenv("PASS_LOCAL_STDERR")) + perror("write to agent"); + exit(1); + } - close(trigger_fd); + close(trigger_fd); - abs_exec_path = strdup(argv[3]); - argv[3] = get_program_name(argv[3]); - execv(abs_exec_path, argv + 3); - perror("execv"); - return 1; + abs_exec_path = strdup(argv[3]); + argv[3] = get_program_name(argv[3]); + execv(abs_exec_path, argv + 3); + perror("execv"); + return 1; }