From b98dffc965c73d18e6baa28728f959fa6300e761 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 4 Mar 2011 16:32:58 +0100 Subject: [PATCH 01/86] qrexec* tools, initial version --- qrexec/Makefile | 13 ++ qrexec/buffer.c | 93 ++++++++ qrexec/buffer.h | 32 +++ qrexec/exec.c | 74 +++++++ qrexec/glue.h | 49 +++++ qrexec/ioall.c | 67 ++++++ qrexec/qrexec.h | 58 +++++ qrexec/qrexec_agent.c | 452 +++++++++++++++++++++++++++++++++++++++ qrexec/qrexec_client.c | 236 ++++++++++++++++++++ qrexec/qrexec_daemon.c | 319 +++++++++++++++++++++++++++ qrexec/txrx-vchan.c | 207 ++++++++++++++++++ qrexec/unix_server.c | 70 ++++++ qrexec/write_stdin.c | 85 ++++++++ rpm_spec/core-appvm.spec | 3 + rpm_spec/core-dom0.spec | 5 + rpm_spec/core-netvm.spec | 3 + 16 files changed, 1766 insertions(+) create mode 100644 qrexec/Makefile create mode 100644 qrexec/buffer.c create mode 100644 qrexec/buffer.h create mode 100644 qrexec/exec.c create mode 100644 qrexec/glue.h create mode 100644 qrexec/ioall.c create mode 100644 qrexec/qrexec.h create mode 100644 qrexec/qrexec_agent.c create mode 100644 qrexec/qrexec_client.c create mode 100644 qrexec/qrexec_daemon.c create mode 100644 qrexec/txrx-vchan.c create mode 100644 qrexec/unix_server.c create mode 100644 qrexec/write_stdin.c diff --git a/qrexec/Makefile b/qrexec/Makefile new file mode 100644 index 00000000..72089fda --- /dev/null +++ b/qrexec/Makefile @@ -0,0 +1,13 @@ +CC=gcc +CFLAGS+=-g -Wall +XENLIBS=-lvchan -lxenstore -lxenctrl + +all: qrexec_daemon qrexec_agent qrexec_client +qrexec_daemon: qrexec_daemon.o unix_server.o ioall.o txrx-vchan.o buffer.o write_stdin.o + $(CC) -g -o qrexec_daemon qrexec_daemon.o unix_server.o ioall.o txrx-vchan.o write_stdin.o buffer.o $(XENLIBS) +qrexec_agent: qrexec_agent.o ioall.o exec.o txrx-vchan.o write_stdin.o buffer.o + $(CC) -g -o qrexec_agent qrexec_agent.o ioall.o exec.o txrx-vchan.o write_stdin.o buffer.o $(XENLIBS) +qrexec_client: qrexec_client.o ioall.o exec.o + $(CC) -g -o qrexec_client qrexec_client.o ioall.o exec.o +clean: + rm -f *.o *~ qrexec_daemon qrexec_agent qrexec_client diff --git a/qrexec/buffer.c b/qrexec/buffer.c new file mode 100644 index 00000000..be36a039 --- /dev/null +++ b/qrexec/buffer.c @@ -0,0 +1,93 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +#include +#include +#include "buffer.h" + +#define BUFFER_LIMIT 50000000 +static int total_mem; +static char *limited_malloc(int len) +{ + char *ret; + total_mem += len; + if (total_mem > BUFFER_LIMIT) { + fprintf(stderr, "attempt to allocate >BUFFER_LIMIT\n"); + exit(1); + } + ret = malloc(len); + if (!ret) { + perror("malloc"); + exit(1); + } + return ret; +} + +static void limited_free(char *ptr, int len) +{ + free(ptr); + total_mem -= len; +} + +void buffer_init(struct buffer *b) +{ + b->buflen = 0; + b->data = NULL; +} + +void buffer_free(struct buffer *b) +{ + if (b->buflen) + limited_free(b->data, b->buflen); + buffer_init(b); +} + +void buffer_append(struct buffer *b, char *data, int len) +{ + int newsize = len + b->buflen; + char *qdata = limited_malloc(len + b->buflen); + memcpy(qdata, b->data, b->buflen); + memcpy(qdata + b->buflen, data, len); + buffer_free(b); + b->buflen = newsize; + b->data = qdata; +} + +void buffer_remove(struct buffer *b, int len) +{ + int newsize = b->buflen - len; + char *qdata = limited_malloc(newsize); + memcpy(qdata, b->data + len, newsize); + buffer_free(b); + b->buflen = newsize; + b->data = qdata; +} + +int buffer_len(struct buffer *b) +{ + return b->buflen; +} + +void *buffer_data(struct buffer *b) +{ + return b->data; +} diff --git a/qrexec/buffer.h b/qrexec/buffer.h new file mode 100644 index 00000000..80b3779d --- /dev/null +++ b/qrexec/buffer.h @@ -0,0 +1,32 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +struct buffer { + char *data; + int buflen; +}; + +void buffer_init(struct buffer *b); +void buffer_free(struct buffer *b); +void buffer_append(struct buffer *b, char *data, int len); +void buffer_remove(struct buffer *b, int len); +int buffer_len(struct buffer *b); +void *buffer_data(struct buffer *b); diff --git a/qrexec/exec.c b/qrexec/exec.c new file mode 100644 index 00000000..a87f5050 --- /dev/null +++ b/qrexec/exec.c @@ -0,0 +1,74 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +#include +#include + +extern void do_exec(char *); + +void fix_fds(int fdin, int fdout, int fderr) +{ + int i; + for (i = 0; i < 256; i++) + if (i != fdin && i != fdout && i != fderr) + close(i); + dup2(fdin, 0); + dup2(fdout, 1); + dup2(fderr, 2); + close(fdin); + close(fdout); + if (fderr != 2) + close(fderr); +} + +void do_fork_exec(char *cmdline, int *pid, int *stdin_fd, int *stdout_fd, + int *stderr_fd) +{ + int inpipe[2], outpipe[2], errpipe[2]; + + if (pipe(inpipe) || pipe(outpipe) || (stderr_fd && pipe(errpipe))) { + perror("pipe"); + exit(1); + } + switch (*pid = fork()) { + case -1: + perror("fork"); + exit(-1); + case 0: + if (stderr_fd) { + fix_fds(inpipe[0], outpipe[1], errpipe[1]); + } else + fix_fds(inpipe[0], outpipe[1], 2); + + do_exec(cmdline); + exit(-1); + default:; + } + close(inpipe[0]); + close(outpipe[1]); + *stdin_fd = inpipe[1]; + *stdout_fd = outpipe[0]; + if (stderr_fd) { + close(errpipe[1]); + *stderr_fd = errpipe[0]; + } +} diff --git a/qrexec/glue.h b/qrexec/glue.h new file mode 100644 index 00000000..228b6866 --- /dev/null +++ b/qrexec/glue.h @@ -0,0 +1,49 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include + +void do_fork_exec(char *cmdline, int *pid, int *stdin_fd, int *stdout_fd, + int *stderr_fd); +int write_all(int fd, void *buf, int size); +int read_all(int fd, void *buf, int size); +int peer_server_init(int port); +char *peer_client_init(int dom, int port); +void wait_for_vchan_or_argfd(int max, fd_set * rdset, fd_set * wrset); +int read_ready_vchan_ext(); +int read_all_vchan_ext(void *buf, int size); +int write_all_vchan_ext(void *buf, int size); +int buffer_space_vchan_ext(); +void fix_fds(int fdin, int fdout, int fderr); + +int get_server_socket(int domid); +int do_accept(int s); +void set_nonblock(int fd); + +enum { + WRITE_STDIN_OK = 0x200, + WRITE_STDIN_BUFFERED, + WRITE_STDIN_ERROR +}; + +int flush_client_data(int fd, int clid, struct buffer *buffer); +int write_stdin(int fd, int clid, char *data, int len, + struct buffer *buffer); diff --git a/qrexec/ioall.c b/qrexec/ioall.c new file mode 100644 index 00000000..9e8d7a3c --- /dev/null +++ b/qrexec/ioall.c @@ -0,0 +1,67 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +#include +#include +#include + +int write_all(int fd, void *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = write(fd, (char *) buf + written, size - written); + if (ret <= 0) { + perror("write"); + return 0; + } + written += ret; + } +// fprintf(stderr, "sent %d bytes\n", size); + return 1; +} + +int read_all(int fd, void *buf, int size) +{ + int got_read = 0; + int ret; + while (got_read < size) { + ret = read(fd, (char *) buf + got_read, size - got_read); + if (ret == 0) { + fprintf(stderr, "EOF\n"); + return 0; + } + if (ret < 0) { + perror("read"); + return 0; + } + got_read += ret; + } +// fprintf(stderr, "read %d bytes\n", size); + return 1; +} + +void set_nonblock(int fd) +{ + int fl = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, fl | O_NONBLOCK); +} diff --git a/qrexec/qrexec.h b/qrexec/qrexec.h new file mode 100644 index 00000000..729ff932 --- /dev/null +++ b/qrexec/qrexec.h @@ -0,0 +1,58 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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 QREXEC_DAEMON_SOCKET_DIR "/var/run/qubes" +#define MAX_FDS 256 +#define MAX_DATA_CHUNK 4096 + +#define REXEC_PORT 512 + +enum { + MSG_CLIENT_TO_SERVER_EXEC_CMDLINE = 0x100, + MSG_CLIENT_TO_SERVER_JUST_EXEC, + + MSG_SERVER_TO_AGENT_EXEC_CMDLINE, + MSG_SERVER_TO_AGENT_JUST_EXEC, + MSG_SERVER_TO_AGENT_INPUT, + MSG_SERVER_TO_AGENT_CLIENT_END, + + MSG_XOFF, + MSG_XON, + + MSG_AGENT_TO_SERVER_STDOUT, + MSG_AGENT_TO_SERVER_STDERR, + MSG_AGENT_TO_SERVER_EXIT_CODE, + + MSG_SERVER_TO_CLIENT_STDOUT, + MSG_SERVER_TO_CLIENT_STDERR, + MSG_SERVER_TO_CLIENT_EXIT_CODE +}; + +struct server_header { + unsigned int type; + unsigned int clid; + unsigned int len; +}; + +struct client_header { + unsigned int type; + unsigned int len; +}; diff --git a/qrexec/qrexec_agent.c b/qrexec/qrexec_agent.c new file mode 100644 index 00000000..1b6f7bad --- /dev/null +++ b/qrexec/qrexec_agent.c @@ -0,0 +1,452 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qrexec.h" +#include "buffer.h" +#include "glue.h" + +enum fdtype { + FDTYPE_INVALID, + FDTYPE_STDOUT, + FDTYPE_STDERR +}; + +struct _process_fd { + int clid; + int type; + int is_blocked; +}; +struct _client_info { + int stdin_fd; + int stdout_fd; + int stderr_fd; + + int pid; + int is_blocked; + struct buffer buffer; +}; + +int max_process_fd = -1; + +/* indexed by file descriptor */ +struct _process_fd process_fd[MAX_FDS]; + +/* indexed by client id, which is descriptor number of a client in daemon */ +struct _client_info client_info[MAX_FDS]; + +void init() +{ + peer_server_init(REXEC_PORT); +} + +void do_exec(char *cmd) +{ + char *sep = index(cmd, ':'); + if (!sep) { + fprintf(stderr, + "cmdline is supposed to be in user:command form\n"); + exit(1); + } + *sep = 0; + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + + execl("/bin/su", "su", "-", cmd, "-c", sep + 1, NULL); + perror("execl"); + exit(1); +} + +void handle_just_exec(int clid, int len) +{ + char buf[len]; + int fdn, pid; + + read_all_vchan_ext(buf, len); + 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 handle_exec(int clid, int len) +{ + char buf[len]; + int pid, stdin_fd, stdout_fd, stderr_fd; + + read_all_vchan_ext(buf, len); + + do_fork_exec(buf, &pid, &stdin_fd, &stdout_fd, &stderr_fd); + + process_fd[stdout_fd].clid = clid; + process_fd[stdout_fd].type = FDTYPE_STDOUT; + process_fd[stdout_fd].is_blocked = 0; + process_fd[stderr_fd].clid = clid; + 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; + + set_nonblock(stdin_fd); + + client_info[clid].stdin_fd = stdin_fd; + client_info[clid].stdout_fd = stdout_fd; + client_info[clid].stderr_fd = stderr_fd; + client_info[clid].pid = pid; + client_info[clid].is_blocked = 0; + buffer_init(&client_info[clid].buffer); + + fprintf(stderr, "executed %s pid %d\n", buf, pid); + +} + + +void update_max_process_fd() +{ + int i; + for (i = max_process_fd; + process_fd[i].type == FDTYPE_INVALID && i >= 0; i--); + max_process_fd = i; +} + +void send_exit_code(int clid, int status) +{ + struct server_header s_hdr; + s_hdr.type = MSG_AGENT_TO_SERVER_EXIT_CODE; + s_hdr.clid = clid; + s_hdr.len = sizeof status; + write_all_vchan_ext(&s_hdr, sizeof s_hdr); + write_all_vchan_ext(&status, sizeof(status)); + fprintf(stderr, "send exit code for clid %d pid %d\n", clid, + client_info[clid].pid); +} + + +// erase process data structures, possibly forced by remote +void remove_process(int clid, int status) +{ + int i; + if (!client_info[clid].pid) + return; + kill(client_info[clid].pid, SIGKILL); + + if (status != -1) + send_exit_code(clid, status); + + + close(client_info[clid].stdin_fd); + client_info[clid].pid = 0; + client_info[clid].stdin_fd = -1; + client_info[clid].is_blocked = 0; + buffer_free(&client_info[clid].buffer); + + for (i = 0; i <= max_process_fd; i++) + if (process_fd[i].type != FDTYPE_INVALID + && process_fd[i].clid == clid) { + process_fd[i].type = FDTYPE_INVALID; + process_fd[i].clid = -1; + process_fd[i].is_blocked = 0; + close(i); + } + update_max_process_fd(); +} + +void handle_input(int clid, int len) +{ + char buf[len]; + + read_all_vchan_ext(buf, len); + if (!client_info[clid].pid) + return; + + if (len == 0) { + close(client_info[clid].stdin_fd); + client_info[clid].stdin_fd = -1; + return; + } + + switch (write_stdin + (client_info[clid].stdin_fd, clid, buf, len, + &client_info[clid].buffer)) { + case WRITE_STDIN_OK: + break; + case WRITE_STDIN_BUFFERED: + client_info[clid].is_blocked = 1; + break; + case WRITE_STDIN_ERROR: + remove_process(clid, 128); + break; + default: + fprintf(stderr, "unknown write_stdin?\n"); + exit(1); + } + +} + +void set_blocked_outerr(int clid, int val) +{ + process_fd[client_info[clid].stdout_fd].is_blocked = val; + process_fd[client_info[clid].stderr_fd].is_blocked = val; +} + +void handle_server_data() +{ + struct server_header s_hdr; + read_all_vchan_ext(&s_hdr, sizeof s_hdr); + +// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.clid, +// s_hdr.len); + + switch (s_hdr.type) { + case MSG_XON: + set_blocked_outerr(s_hdr.clid, 0); + break; + case MSG_XOFF: + set_blocked_outerr(s_hdr.clid, 1); + break; + case MSG_SERVER_TO_AGENT_EXEC_CMDLINE: + handle_exec(s_hdr.clid, s_hdr.len); + break; + case MSG_SERVER_TO_AGENT_JUST_EXEC: + handle_just_exec(s_hdr.clid, s_hdr.len); + break; + case MSG_SERVER_TO_AGENT_INPUT: + handle_input(s_hdr.clid, s_hdr.len); + break; + case MSG_SERVER_TO_AGENT_CLIENT_END: + remove_process(s_hdr.clid, -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; + int len; + + len = buffer_space_vchan_ext(); + if (len <= sizeof s_hdr) + return; + + ret = read(fd, buf, len - sizeof s_hdr); + s_hdr.clid = process_fd[fd].clid; + + 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, clid=%d, type=%d ?\n", fd, + process_fd[fd].clid, process_fd[fd].type); + exit(1); + } + s_hdr.len = ret; + if (ret >= 0) { + write_all_vchan_ext(&s_hdr, sizeof s_hdr); + write_all_vchan_ext(buf, ret); + } + if (ret == 0) { + process_fd[fd].type = FDTYPE_INVALID; + process_fd[fd].clid = -1; + process_fd[fd].is_blocked = 0; + close(fd); + update_max_process_fd(); + } + if (ret < 0) + remove_process(process_fd[fd].clid, 127); +} + +volatile int child_exited; + +void sigchld_handler(int x) +{ + 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; +} + + +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); +} + + +void flush_out_err(int clid) +{ + fd_set select_set; + int fd_max = -1; + int i; + int ret; + struct timeval tv; + for (;;) { + FD_ZERO(&select_set); + for (i = 0; i <= max_process_fd; i++) { + if (process_fd[i].type != FDTYPE_INVALID + && !process_fd[i].is_blocked + && process_fd[i].clid == clid) { + FD_SET(i, &select_set); + fd_max = i; + } + } + if (fd_max == -1) + return; + tv.tv_sec = 0; + tv.tv_usec = 0; + ret = select(fd_max + 1, &select_set, NULL, NULL, &tv); + if (ret < 0 && errno != EINTR) { + perror("select"); + exit(1); + } + if (!ret) + return; + handle_process_data_all(&select_set); + } +} + +void reap_children() +{ + int status; + int pid; + int clid; + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + clid = find_info(pid); + if (clid < 0) + continue; + flush_out_err(clid); + remove_process(clid, status); + } + 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); + + 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_FDS; i++) + if (client_info[i].pid > 0 && 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 clid) +{ + struct _client_info *info = &client_info[clid]; + switch (flush_client_data(info->stdin_fd, clid, &info->buffer)) { + case WRITE_STDIN_OK: + info->is_blocked = 0; + break; + case WRITE_STDIN_ERROR: + remove_process(clid, 128); + break; + case WRITE_STDIN_BUFFERED: + break; + default: + fprintf(stderr, "unknown flush_client_data?\n"); + exit(1); + } +} + + +int main() +{ + fd_set rdset, wrset; + int max; + int i; + + init(); + signal(SIGCHLD, sigchld_handler); + signal(SIGPIPE, SIG_IGN); + + + for (;;) { + max = fill_fds_for_select(&rdset, &wrset); + if (buffer_space_vchan_ext() <= + sizeof(struct server_header)) + FD_ZERO(&rdset); + + wait_for_vchan_or_argfd(max, &rdset, &wrset); + + while (read_ready_vchan_ext()) + handle_server_data(); + + handle_process_data_all(&rdset); + for (i = 0; i <= MAX_FDS; i++) + if (client_info[i].pid > 0 + && client_info[i].is_blocked + && FD_ISSET(client_info[i].stdin_fd, &wrset)) + flush_client_data_agent(i); + + if (child_exited) + reap_children(); + } +} diff --git a/qrexec/qrexec_client.c b/qrexec/qrexec_client.c new file mode 100644 index 00000000..e2878480 --- /dev/null +++ b/qrexec/qrexec_client.c @@ -0,0 +1,236 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include "qrexec.h" +#include "buffer.h" +#include "glue.h" + +int connect_unix_socket(int domid) +{ + int s, len; + struct sockaddr_un remote; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + perror("socket"); + return -1; + } + + remote.sun_family = AF_UNIX; + snprintf(remote.sun_path, sizeof remote.sun_path, + QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", domid); + len = strlen(remote.sun_path) + sizeof(remote.sun_family); + if (connect(s, (struct sockaddr *) &remote, len) == -1) { + perror("connect"); + exit(1); + } + return s; +} + +void do_exec(char *prog) +{ + execl("/bin/bash", "bash", "-c", prog, NULL); +} + +int local_stdin_fd, local_stdout_fd; + +void prepare_local_fds(char *cmdline) +{ + int pid; + if (!cmdline) { + local_stdin_fd = 1; + local_stdout_fd = 0; + return; + } + do_fork_exec(cmdline, &pid, &local_stdin_fd, &local_stdout_fd, + NULL); +} + + +void send_cmdline(int s, int type, char *cmdline) +{ + struct client_header hdr; + hdr.type = type; + hdr.len = strlen(cmdline) + 1; + if (!write_all(s, &hdr, sizeof(hdr)) + || !write_all(s, cmdline, hdr.len)) { + perror("write daemon"); + exit(1); + } +} + +void handle_input(int s) +{ + char buf[MAX_DATA_CHUNK]; + int ret; + ret = read(local_stdout_fd, buf, sizeof(buf)); + if (ret < 0) { + perror("read"); + exit(1); + } + if (ret == 0) { + local_stdout_fd = -1; + shutdown(s, SHUT_WR); + } + if (!write_all(s, buf, ret)) { + perror("write daemon"); + exit(1); + } +} + +void handle_daemon_data(int s) +{ + int status; + struct client_header hdr; + char buf[MAX_DATA_CHUNK]; + + if (!read_all(s, &hdr, sizeof hdr)) { + perror("read daemon"); + exit(1); + } + if (hdr.len > MAX_DATA_CHUNK) { + fprintf(stderr, "client_header.len=%d\n", hdr.len); + exit(1); + } + if (!read_all(s, buf, hdr.len)) { + perror("read daemon"); + exit(1); + } + + switch (hdr.type) { + case MSG_SERVER_TO_CLIENT_STDOUT: + if (hdr.len == 0) + close(local_stdin_fd); + else + write_all(local_stdin_fd, buf, hdr.len); + break; + case MSG_SERVER_TO_CLIENT_STDERR: + write_all(2, buf, hdr.len); + break; + case MSG_SERVER_TO_CLIENT_EXIT_CODE: + status = *(unsigned int *) buf; + if (WIFEXITED(status)) + exit(WEXITSTATUS(status)); + else + exit(255); + break; + default: + fprintf(stderr, "unknown msg %d\n", hdr.type); + exit(1); + } +} + +// perhaps we could save a syscall if we include both sides in both +// rdset and wrset; to be investigated +void handle_daemon_only_until_writable(s) +{ + fd_set rdset, wrset; + + do { + FD_ZERO(&rdset); + FD_ZERO(&wrset); + FD_SET(s, &rdset); + FD_SET(s, &wrset); + + if (select(s + 1, &rdset, &wrset, NULL, NULL) < 0) { + perror("select"); + exit(1); + } + if (FD_ISSET(s, &rdset)) + handle_daemon_data(s); + } while (!FD_ISSET(s, &wrset)); +} + +void select_loop(int s) +{ + fd_set select_set; + int max; + for (;;) { + handle_daemon_only_until_writable(s); + FD_ZERO(&select_set); + FD_SET(s, &select_set); + max = s; + if (local_stdout_fd != -1) { + FD_SET(local_stdout_fd, &select_set); + if (s < local_stdout_fd) + max = local_stdout_fd; + } + if (select(max + 1, &select_set, NULL, NULL, NULL) < 0) { + perror("select"); + exit(1); + } + if (FD_ISSET(s, &select_set)) + handle_daemon_data(s); + if (local_stdout_fd != -1 + && FD_ISSET(local_stdout_fd, &select_set)) + handle_input(s); + } +} + +void usage(char *name) +{ + fprintf(stderr, + "usage: %s -d domain_num [-l local_prog] -e remote_cmdline\n" + "-e means exit after sending cmd\n", name); + exit(1); +} + +int main(int argc, char **argv) +{ + int opt; + int domid = 0; + int s; + int just_exec = 0; + char *local_cmdline = NULL; + while ((opt = getopt(argc, argv, "d:l:e")) != -1) { + switch (opt) { + case 'd': + domid = atoi(optarg); + break; + case 'l': + local_cmdline = strdup(optarg); + break; + case 'e': + just_exec = 1; + break; + default: + usage(argv[0]); + } + } + if (optind >= argc || !domid) + usage(argv[0]); + s = connect_unix_socket(domid); + prepare_local_fds(local_cmdline); + if (just_exec) + send_cmdline(s, MSG_CLIENT_TO_SERVER_JUST_EXEC, + argv[optind]); + else { + send_cmdline(s, MSG_CLIENT_TO_SERVER_EXEC_CMDLINE, + argv[optind]); + select_loop(s); + } + return 0; +} diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c new file mode 100644 index 00000000..ef6ec05f --- /dev/null +++ b/qrexec/qrexec_daemon.c @@ -0,0 +1,319 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include "qrexec.h" +#include "buffer.h" +#include "glue.h" + +enum client_flags { + CLIENT_INVALID = 0, + CLIENT_CMDLINE = 1, + CLIENT_DATA = 2, + CLIENT_DONT_READ = 4, + CLIENT_OUTQ_FULL = 8 +}; + +struct _client { + int state; + struct buffer buffer; +}; + +struct _client clients[MAX_FDS]; + +int max_client_fd = -1; +int server_fd; + +void init(int xid) +{ + if (xid <= 0) { + fprintf(stderr, "domain id=0?\n"); + exit(1); + } + server_fd = get_server_socket(xid); + peer_client_init(xid, REXEC_PORT); + setuid(getuid()); + signal(SIGPIPE, SIG_IGN); +} + +void handle_new_client() +{ + int fd = do_accept(server_fd); + if (fd >= MAX_FDS) { + fprintf(stderr, "too many clients ?\n"); + exit(1); + } + clients[fd].state = CLIENT_CMDLINE; + buffer_init(&clients[fd].buffer); + if (fd > max_client_fd) + max_client_fd = fd; +} + +void flush_client(int fd) +{ + int i; + struct server_header s_hdr; + close(fd); + clients[fd].state = CLIENT_INVALID; + buffer_free(&clients[fd].buffer); + if (max_client_fd == fd) { + for (i = fd; clients[i].state == CLIENT_INVALID && i >= 0; + i--); + max_client_fd = i; + } + s_hdr.type = MSG_SERVER_TO_AGENT_CLIENT_END; + s_hdr.clid = fd; + s_hdr.len = 0; + write_all_vchan_ext(&s_hdr, sizeof(s_hdr)); +} + +void pass_to_agent(int fd, struct server_header *s_hdr) +{ + int len = s_hdr->len; + char buf[len]; + if (!read_all(fd, buf, len)) { + flush_client(fd); + return; + } + write_all_vchan_ext(s_hdr, sizeof(*s_hdr)); + write_all_vchan_ext(buf, len); +} + +void handle_client_cmdline(int fd) +{ + struct client_header hdr; + struct server_header s_hdr; + if (!read_all(fd, &hdr, sizeof hdr)) { + flush_client(fd); + return; + } + switch (hdr.type) { + case MSG_CLIENT_TO_SERVER_EXEC_CMDLINE: + s_hdr.type = MSG_SERVER_TO_AGENT_EXEC_CMDLINE; + break; + case MSG_CLIENT_TO_SERVER_JUST_EXEC: + s_hdr.type = MSG_SERVER_TO_AGENT_JUST_EXEC; + break; + default: + flush_client(fd); + return; + } + + s_hdr.clid = fd; + s_hdr.len = hdr.len; + pass_to_agent(fd, &s_hdr); + clients[fd].state = CLIENT_DATA; + set_nonblock(fd); + if (hdr.type == MSG_CLIENT_TO_SERVER_JUST_EXEC) + flush_client(fd); + +} + +void handle_client_data(int fd) +{ + struct server_header s_hdr; + char buf[MAX_DATA_CHUNK]; + int len, ret; + + if (clients[fd].state == CLIENT_CMDLINE) { + handle_client_cmdline(fd); + return; + } + len = buffer_space_vchan_ext(); + if (len <= sizeof s_hdr) + return; + ret = read(fd, buf, len - sizeof(s_hdr)); + if (ret < 0) { + perror("read client"); + flush_client(fd); + return; + } + s_hdr.clid = fd; + s_hdr.len = ret; + s_hdr.type = MSG_SERVER_TO_AGENT_INPUT; + + write_all_vchan_ext(&s_hdr, sizeof(s_hdr)); + write_all_vchan_ext(buf, ret); + if (ret == 0) + clients[fd].state |= CLIENT_DONT_READ; +} + +void flush_client_data_daemon(int clid) +{ + switch (flush_client_data(clid, clid, &clients[clid].buffer)) { + case WRITE_STDIN_OK: + clients[clid].state &= ~CLIENT_OUTQ_FULL; + break; + case WRITE_STDIN_ERROR: + flush_client(clid); + break; + case WRITE_STDIN_BUFFERED: + break; + default: + fprintf(stderr, "unknown flush_client_data?\n"); + exit(1); + } +} + +void pass_to_client(int clid, struct client_header *hdr) +{ + int len = hdr->len; + char buf[sizeof(*hdr) + len]; + + *(struct client_header *) buf = *hdr; + read_all_vchan_ext(buf + sizeof(*hdr), len); + + switch (write_stdin + (clid, clid, buf, len + sizeof(*hdr), + &clients[clid].buffer)) { + case WRITE_STDIN_OK: + break; + case WRITE_STDIN_BUFFERED: + clients[clid].state |= CLIENT_OUTQ_FULL; + break; + case WRITE_STDIN_ERROR: + flush_client(clid); + break; + default: + fprintf(stderr, "unknown write_stdin?\n"); + exit(1); + } +} + +void handle_agent_data() +{ + struct client_header hdr; + struct server_header s_hdr; + read_all_vchan_ext(&s_hdr, sizeof s_hdr); + +// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.clid, +// s_hdr.len); + + if (s_hdr.clid >= MAX_FDS || s_hdr.clid < 0) { + fprintf(stderr, "from agent: clid=%d\n", s_hdr.clid); + exit(1); + } + + if (s_hdr.type == MSG_XOFF) { + clients[s_hdr.clid].state |= CLIENT_DONT_READ; + return; + } + if (s_hdr.type == MSG_XON) { + clients[s_hdr.clid].state &= ~CLIENT_DONT_READ; + return; + } + + switch (s_hdr.type) { + case MSG_AGENT_TO_SERVER_STDOUT: + hdr.type = MSG_SERVER_TO_CLIENT_STDOUT; + break; + case MSG_AGENT_TO_SERVER_STDERR: + hdr.type = MSG_SERVER_TO_CLIENT_STDERR; + break; + case MSG_AGENT_TO_SERVER_EXIT_CODE: + hdr.type = MSG_SERVER_TO_CLIENT_EXIT_CODE; + break; + default: + fprintf(stderr, "from agent: type=%d\n", s_hdr.type); + exit(1); + } + hdr.len = s_hdr.len; + if (hdr.len > MAX_DATA_CHUNK) { + fprintf(stderr, "agent feeded %d of data bytes?\n", + hdr.len); + exit(1); + } + if (clients[s_hdr.clid].state == CLIENT_INVALID) { + // benefit of doubt - maybe client exited earlier + char buf[MAX_DATA_CHUNK]; + read_all_vchan_ext(buf, s_hdr.len); + return; + } + pass_to_client(s_hdr.clid, &hdr); + if (s_hdr.type == MSG_AGENT_TO_SERVER_EXIT_CODE) + flush_client(s_hdr.clid); +} + +int fill_fds_for_select(fd_set * rdset, fd_set * wrset) +{ + int i; + int max = -1; + FD_ZERO(rdset); + FD_ZERO(wrset); + for (i = 0; i <= max_client_fd; i++) { + if (clients[i].state != CLIENT_INVALID + && !(clients[i].state & CLIENT_DONT_READ)) { + FD_SET(i, rdset); + max = i; + } + if (clients[i].state != CLIENT_INVALID + && clients[i].state & CLIENT_OUTQ_FULL) { + FD_SET(i, wrset); + max = i; + } + } + FD_SET(server_fd, rdset); + if (server_fd > max) + max = server_fd; + return max; +} + +int main(int argc, char **argv) +{ + fd_set rdset, wrset; + int i; + int max; + + if (argc != 2) { + fprintf(stderr, "usage: %s domainid\n", argv[0]); + exit(1); + } + init(atoi(argv[1])); + for (;;) { + max = fill_fds_for_select(&rdset, &wrset); + if (buffer_space_vchan_ext() <= + sizeof(struct server_header)) + FD_ZERO(&rdset); + + wait_for_vchan_or_argfd(max, &rdset, &wrset); + + if (FD_ISSET(server_fd, &rdset)) + handle_new_client(); + + while (read_ready_vchan_ext()) + handle_agent_data(); + + for (i = 0; i <= max_client_fd; i++) + if (clients[i].state != CLIENT_INVALID + && FD_ISSET(i, &rdset)) + handle_client_data(i); + + for (i = 0; i <= max_client_fd; i++) + if (clients[i].state != CLIENT_INVALID + && FD_ISSET(i, &wrset)) + flush_client_data_daemon(i); + } +} diff --git a/qrexec/txrx-vchan.c b/qrexec/txrx-vchan.c new file mode 100644 index 00000000..2a95180f --- /dev/null +++ b/qrexec/txrx-vchan.c @@ -0,0 +1,207 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +static struct libvchan *ctrl; +static int is_server; +int write_all_vchan_ext(void *buf, int size) +{ + int written = 0; + int ret; + + while (written < size) { + ret = + libvchan_write(ctrl, (char *) buf + written, + size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } +// fprintf(stderr, "sent %d bytes\n", size); + return size; +} + + +int read_all_vchan_ext(void *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = + libvchan_read(ctrl, (char *) buf + written, + size - written); + if (ret == 0) { + fprintf(stderr, "EOF\n"); + exit(1); + } + if (ret < 0) { + perror("read"); + exit(1); + } + written += ret; + } +// fprintf(stderr, "read %d bytes\n", size); + return size; +} + +int read_ready_vchan_ext() +{ + return libvchan_data_ready(ctrl); +} + +int buffer_space_vchan_ext() +{ + return libvchan_buffer_space(ctrl); +} + +// if the remote domain is destroyed, we get no notification +// thus, we check for the status periodically + +static int xc_handle = -1; +void slow_check_for_libvchan_is_eof(struct libvchan *ctrl) +{ + struct evtchn_status evst; + evst.port = ctrl->evport; + evst.dom = DOMID_SELF; + if (xc_evtchn_status(xc_handle, &evst)) { + perror("xc_evtchn_status"); + exit(1); + } + if (evst.status != EVTCHNSTAT_interdomain) { + fprintf(stderr, "event channel disconnected\n"); + exit(0); + } +} + + +int wait_for_vchan_or_argfd_once(int max, fd_set * rdset, fd_set * wrset) +{ + int vfd, ret; + struct timeval tv = { 1, 100000 }; + vfd = libvchan_fd_for_select(ctrl); + FD_SET(vfd, rdset); + if (vfd > max) + max = vfd; + max++; + ret = select(max, rdset, wrset, NULL, &tv); + if (ret < 0) { + if (errno != EINTR) { + perror("select"); + exit(1); + } else { + FD_ZERO(rdset); + FD_ZERO(wrset); + fprintf(stderr, "eintr\n"); + return 1; + } + + } + if (libvchan_is_eof(ctrl)) { + fprintf(stderr, "libvchan_is_eof\n"); + exit(0); + } + if (!is_server && ret == 0) + slow_check_for_libvchan_is_eof(ctrl); + if (FD_ISSET(vfd, rdset)) + // the following will never block; we need to do this to + // clear libvchan_fd pending state + libvchan_wait(ctrl); + return ret; +} + +void wait_for_vchan_or_argfd(int max, fd_set * rdset, fd_set * wrset) +{ + fd_set r = *rdset, w = *wrset; + do { + *rdset = r; + *wrset = w; + } + while (wait_for_vchan_or_argfd_once(max, rdset, wrset) == 0); +} + +int peer_server_init(int port) +{ + is_server = 1; + ctrl = libvchan_server_init(port); + if (!ctrl) { + perror("libvchan_server_init"); + exit(1); + } + return 0; +} + +char *peer_client_init(int dom, int port) +{ + struct xs_handle *xs; + char buf[64]; + char *name; + char *dummy; + unsigned int len = 0; + char devbuf[128]; + unsigned int count; + char **vec; + +// double_buffered = 1; // writes to vchan are buffered, nonblocking +// double_buffer_init(); + xs = xs_daemon_open(); + if (!xs) { + perror("xs_daemon_open"); + exit(1); + } + snprintf(buf, sizeof(buf), "/local/domain/%d/name", dom); + name = xs_read(xs, 0, buf, &len); + if (!name) { + perror("xs_read domainname"); + exit(1); + } + snprintf(devbuf, sizeof(devbuf), + "/local/domain/%d/device/vchan/%d/event-channel", dom, + port); + xs_watch(xs, devbuf, devbuf); + do { + vec = xs_read_watch(xs, &count); + if (vec) + free(vec); + len = 0; + dummy = xs_read(xs, 0, devbuf, &len); + } + while (!dummy || !len); // wait for the server to create xenstore entries + free(dummy); + xs_daemon_close(xs); + + // now client init should succeed; "while" is redundant + while (!(ctrl = libvchan_client_init(dom, port))); + + xc_handle = xc_interface_open(); + if (xc_handle < 0) { + perror("xc_interface_open"); + exit(1); + } + return name; +} diff --git a/qrexec/unix_server.c b/qrexec/unix_server.c new file mode 100644 index 00000000..aedf9167 --- /dev/null +++ b/qrexec/unix_server.c @@ -0,0 +1,70 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include "qrexec.h" + +int get_server_socket(int domid) +{ + struct sockaddr_un sockname; + int s; + char socket_address[40]; + + snprintf(socket_address, sizeof(socket_address), + QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", domid); + unlink(socket_address); + s = socket(AF_UNIX, SOCK_STREAM, 0); + memset(&sockname, 0, sizeof(sockname)); + sockname.sun_family = AF_UNIX; + memcpy(sockname.sun_path, socket_address, strlen(socket_address)); + + if (bind(s, (struct sockaddr *) &sockname, sizeof(sockname)) == -1) { + printf("bind() failed\n"); + close(s); + exit(1); + } +// chmod(sockname.sun_path, 0666); + if (listen(s, 5) == -1) { + perror("listen() failed\n"); + close(s); + exit(1); + } + return s; +} + +int do_accept(int s) +{ + struct sockaddr_un peer; + unsigned int addrlen; + int fd; + addrlen = sizeof(peer); + fd = accept(s, (struct sockaddr *) &peer, &addrlen); + if (fd == -1) { + perror("unix accept"); + exit(1); + } + return fd; +} diff --git a/qrexec/write_stdin.c b/qrexec/write_stdin.c new file mode 100644 index 00000000..9eec24be --- /dev/null +++ b/qrexec/write_stdin.c @@ -0,0 +1,85 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +#include +#include +#include "qrexec.h" +#include "buffer.h" +#include "glue.h" + +int flush_client_data(int fd, int clid, struct buffer *buffer) +{ + int ret; + int len; + for (;;) { + len = buffer_len(buffer); + if (len > MAX_DATA_CHUNK) + len = MAX_DATA_CHUNK; + ret = write(fd, buffer_data(buffer), len); + if (ret == -1) { + if (errno != EAGAIN) { + return WRITE_STDIN_ERROR; + } else + return WRITE_STDIN_BUFFERED; + } + buffer_remove(buffer, len); + len = buffer_len(buffer); + if (!len) { + struct server_header s_hdr; + s_hdr.type = MSG_XON; + s_hdr.clid = clid; + s_hdr.len = 0; + write_all_vchan_ext(&s_hdr, sizeof s_hdr); + return WRITE_STDIN_OK; + } + } + +} + + +int write_stdin(int fd, int clid, char *data, int len, + struct buffer *buffer) +{ + int ret; + ret = write(fd, data, len); + if (ret == len) + return WRITE_STDIN_OK; + if (ret == -1) { + if (errno == EAGAIN) { + struct server_header s_hdr; + buffer_append(buffer, data, len); + + s_hdr.type = MSG_XOFF; + s_hdr.clid = clid; + s_hdr.len = 0; + write_all_vchan_ext(&s_hdr, sizeof s_hdr); + + return WRITE_STDIN_BUFFERED; + } else + return WRITE_STDIN_ERROR; + } else { + fprintf(stderr, + "writes < PIPE_BUF were supposed to be atomic ?\n"); + return WRITE_STDIN_ERROR; + } + +} diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index c9a009d1..6dccb5fb 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -60,6 +60,7 @@ fi %build make clean all make -C ../common +make -C ../qrexec %install @@ -72,6 +73,7 @@ mkdir -p $RPM_BUILD_ROOT/usr/bin cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm $RPM_BUILD_ROOT/usr/bin mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes +cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} @@ -199,6 +201,7 @@ rm -rf $RPM_BUILD_ROOT %{kde_service_dir}/qvm-dvm.desktop %attr(4755,root,root) /usr/lib/qubes/qubes_penctl /usr/lib/qubes/qubes_add_pendrive_script +/usr/lib/qubes/qrexec_agent /etc/udev/rules.d/qubes.rules /etc/sysconfig/iptables /var/lib/qubes diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 9256125b..11182506 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -49,6 +49,7 @@ python -m compileall qvm-core qmemman python -O -m compileall qvm-core qmemman make -C restore make -C ../common +make -C ../qrexec %install @@ -86,6 +87,8 @@ cp aux-tools/reset_vm_configs.py $RPM_BUILD_ROOT/usr/lib/qubes cp pendrive_swapper/qubes_pencmd $RPM_BUILD_ROOT/usr/lib/qubes cp qmemman/server.py $RPM_BUILD_ROOT/usr/lib/qubes/qmemman_daemon.py cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes/ +cp ../qrexec/qrexec_daemon $RPM_BUILD_ROOT/usr/lib/qubes/ +cp ../qrexec/qrexec_client $RPM_BUILD_ROOT/usr/lib/qubes/ cp restore/xenstore-watch restore/qvm-create-default-dvm $RPM_BUILD_ROOT/usr/bin cp restore/qubes_restore restore/xenfreepages $RPM_BUILD_ROOT/usr/lib/qubes @@ -276,6 +279,8 @@ fi /usr/lib/qubes/qubes_prepare_saved_domain.sh /etc/xen/scripts/block.qubes /etc/xen/scripts/vif-route-qubes +/usr/lib/qubes/qrexec_client +%attr(4750,root,qubes) /usr/lib/qubes/qrexec_daemon %attr(4750,root,qubes) /usr/lib/qubes/xenfreepages %attr(2770,root,qubes) %dir /var/log/qubes %attr(770,root,qubes) %dir /var/run/qubes diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index 83d88c29..678da3a9 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -50,6 +50,7 @@ fi %build +make -C ../qrexec %install @@ -61,6 +62,7 @@ mkdir -p $RPM_BUILD_ROOT/etc/init.d cp qubes_core $RPM_BUILD_ROOT/etc/init.d/ mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes +cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes cp ../common/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes cp ../common/qubes_fix_nm_conf.sh $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/etc/dhclient.d @@ -175,6 +177,7 @@ rm -rf $RPM_BUILD_ROOT /etc/sysconfig/iptables /etc/init.d/qubes_core /var/lib/qubes +/usr/lib/qubes/qrexec_agent /usr/lib/qubes/qubes_setup_dnat_to_ns /usr/lib/qubes/qubes_fix_nm_conf.sh /etc/dhclient.d/qubes_setup_dnat_to_ns.sh From d6f327492dd94f91e1f8bf24110783cbc65aad44 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 4 Mar 2011 17:19:51 +0100 Subject: [PATCH 02/86] Start qrexec daemon and agent --- appvm/qubes_core | 2 ++ dom0/qvm-tools/qvm-run | 6 ++++++ dom0/qvm-tools/qvm-start | 6 ++++++ netvm/qubes_core | 3 +++ 4 files changed, 17 insertions(+) diff --git a/appvm/qubes_core b/appvm/qubes_core index 714727a2..6b100294 100755 --- a/appvm/qubes_core +++ b/appvm/qubes_core @@ -85,6 +85,8 @@ start() MEMINFO_DELAY_USEC=100000 /usr/lib/qubes/meminfo-writer $MEM_CHANGE_THRESHOLD_KB $MEMINFO_DELAY_USEC & + /usr/lib/qubes/qrexec_agent 2>/var/log/qubes/qrexec_agent.log & + [ -x /rw/config/rc.local ] && /rw/config/rc.local success echo "" diff --git a/dom0/qvm-tools/qvm-run b/dom0/qvm-tools/qvm-run index 6125ad5d..e6f22343 100755 --- a/dom0/qvm-tools/qvm-run +++ b/dom0/qvm-tools/qvm-run @@ -33,6 +33,7 @@ import time qubes_guid_path = "/usr/bin/qubes_guid" qubes_clipd_path = "/usr/bin/qclipd" qubes_qfilexchgd_path= "/usr/bin/qfilexchgd" +qrexec_daemon_path = "/usr/lib/qubes/qrexec_daemon" notify_object = None # how long (in sec) to wait for VMs to shutdown @@ -78,6 +79,11 @@ def vm_run_cmd(vm, cmd, options): if options.tray: tray_notify ("Starting the '{0}' VM...".format(vm.name), label=vm.label) xid = vm.start(verbose=options.verbose) + retcode = subprocess.call ([qrexec_daemon_path, str(xid)]) + if (retcode != 0) : + print "ERROR: Cannot start qrexec_daemon!" + exit (1) + except (IOError, OSError, QubesException) as err: print "ERROR: {0}".format(err) if options.tray: diff --git a/dom0/qvm-tools/qvm-start b/dom0/qvm-tools/qvm-start index 197a3c2c..cd99f71e 100755 --- a/dom0/qvm-tools/qvm-start +++ b/dom0/qvm-tools/qvm-start @@ -26,6 +26,7 @@ from optparse import OptionParser import subprocess qubes_guid_path = "/usr/bin/qubes_guid" +qrexec_daemon_path = "/usr/lib/qubes/qrexec_daemon" def main(): usage = "usage: %prog [options] " @@ -60,6 +61,11 @@ def main(): print "ERROR: {0}".format(err) exit (1) + retcode = subprocess.call ([qrexec_daemon_path, str(xid)]) + if (retcode != 0) : + print "ERROR: Cannot start qrexec_daemon!" + exit (1) + if options.noguid: exit (0) if options.verbose: diff --git a/netvm/qubes_core b/netvm/qubes_core index dbfaad7a..d7f8594d 100755 --- a/netvm/qubes_core +++ b/netvm/qubes_core @@ -28,6 +28,9 @@ start() echo "NS2=$secondary_dns" >> /var/run/qubes/qubes_ns /usr/lib/qubes/qubes_setup_dnat_to_ns echo "1" > /proc/sys/net/ipv4/ip_forward + + /usr/lib/qubes/qrexec_agent 2>/var/log/qubes/qrexec_agent.log & + success echo "" return 0 From b899bfc9ba231fa6f2465f19f167377588c7efd7 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 4 Mar 2011 17:38:59 +0100 Subject: [PATCH 03/86] Daemonize qrexec_daemon. --- qrexec/qrexec_daemon.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index ef6ec05f..508b164c 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include "qrexec.h" #include "buffer.h" #include "glue.h" @@ -49,10 +51,37 @@ int server_fd; void init(int xid) { + char dbg_log[256]; + int logfd; + if (xid <= 0) { fprintf(stderr, "domain id=0?\n"); exit(1); } + switch (fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + break; + default: + exit(0); + } + close(0); + snprintf(dbg_log, sizeof(dbg_log), + "/var/log/qubes/qrexec.%d.log", xid); + umask(0007); + logfd = open(dbg_log, O_WRONLY | O_CREAT | O_TRUNC, 0640); + umask(0077); + dup2(logfd, 1); + dup2(logfd, 2); + + chdir("/var/run/qubes"); + if (setsid() < 0) { + perror("setsid()"); + exit(1); + } + server_fd = get_server_socket(xid); peer_client_init(xid, REXEC_PORT); setuid(getuid()); @@ -209,8 +238,8 @@ void handle_agent_data() struct server_header s_hdr; read_all_vchan_ext(&s_hdr, sizeof s_hdr); -// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.clid, -// s_hdr.len); +// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.clid, +// s_hdr.len); if (s_hdr.clid >= MAX_FDS || s_hdr.clid < 0) { fprintf(stderr, "from agent: clid=%d\n", s_hdr.clid); From bb0507c89af926c3489450543558d3b981526844 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 4 Mar 2011 17:41:54 +0100 Subject: [PATCH 04/86] Make qrexec_daemon socket accessible. Set restructive umask after socket creation. --- qrexec/qrexec_daemon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index 508b164c..23a01dbf 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -72,7 +72,6 @@ void init(int xid) "/var/log/qubes/qrexec.%d.log", xid); umask(0007); logfd = open(dbg_log, O_WRONLY | O_CREAT | O_TRUNC, 0640); - umask(0077); dup2(logfd, 1); dup2(logfd, 2); @@ -82,7 +81,9 @@ void init(int xid) exit(1); } + umask(0); server_fd = get_server_socket(xid); + umask(0077); peer_client_init(xid, REXEC_PORT); setuid(getuid()); signal(SIGPIPE, SIG_IGN); From 50252ec64e23664b37e50fe85994b4ccb8c41577 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Mon, 7 Mar 2011 13:50:30 +0100 Subject: [PATCH 05/86] qrexec_daemon parent should exit after connection to VM. --- qrexec/qrexec_daemon.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index 23a01dbf..06e63068 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -49,6 +49,11 @@ struct _client clients[MAX_FDS]; int max_client_fd = -1; int server_fd; +void handle_usr1(int x) +{ + exit(0); +} + void init(int xid) { char dbg_log[256]; @@ -58,6 +63,7 @@ void init(int xid) fprintf(stderr, "domain id=0?\n"); exit(1); } + signal(SIGUSR1, handle_usr1); switch (fork()) { case -1: perror("fork"); @@ -65,6 +71,7 @@ void init(int xid) case 0: break; default: + pause(); exit(0); } close(0); @@ -87,6 +94,7 @@ void init(int xid) peer_client_init(xid, REXEC_PORT); setuid(getuid()); signal(SIGPIPE, SIG_IGN); + signal(SIGUSR1, SIG_DFL); } void handle_new_client() From 27c8b057927b398d797fc34202dadb9edb36412a Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Mon, 7 Mar 2011 13:54:57 +0100 Subject: [PATCH 06/86] qrexec_daemon child should notify the parent. --- qrexec/qrexec_daemon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index 06e63068..5ceb4ab9 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -95,6 +95,7 @@ void init(int xid) setuid(getuid()); signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, SIG_DFL); + kill(getppid(), SIGUSR1); } void handle_new_client() From 62d0127647a713d63e29f99a8e64cdaf266d6f48 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Mon, 7 Mar 2011 15:58:04 +0100 Subject: [PATCH 07/86] Integrate qrexec with qvm-run. --- dom0/qvm-tools/qvm-run | 71 ++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/dom0/qvm-tools/qvm-run b/dom0/qvm-tools/qvm-run index e6f22343..b626d929 100755 --- a/dom0/qvm-tools/qvm-run +++ b/dom0/qvm-tools/qvm-run @@ -29,11 +29,14 @@ import socket import errno import dbus import time +import os +import os.path qubes_guid_path = "/usr/bin/qubes_guid" qubes_clipd_path = "/usr/bin/qclipd" qubes_qfilexchgd_path= "/usr/bin/qfilexchgd" qrexec_daemon_path = "/usr/lib/qubes/qrexec_daemon" +qrexec_client_path = "/usr/lib/qubes/qrexec_client" notify_object = None # how long (in sec) to wait for VMs to shutdown @@ -46,6 +49,15 @@ def tray_notify(str, label, timeout = 3000): def tray_notify_error(str, timeout = 3000): notify_object.Notify("Qubes", 0, "dialog-error", "Qubes", str, [], [], timeout, dbus_interface="org.freedesktop.Notifications") +def actually_execute(domid, cmd, options): + args = [qrexec_client_path, "-d", domid, cmd] + if options.localcmd is not None: + args += [ "-l", options.localcmd] + if options.passio and not options.run_on_all_running: + os.execv(qrexec_client_path, args) + exit(1) + args += ["-e"] + subprocess.call(args) def vm_run_cmd(vm, cmd, options): if options.shutdown: @@ -79,10 +91,6 @@ def vm_run_cmd(vm, cmd, options): if options.tray: tray_notify ("Starting the '{0}' VM...".format(vm.name), label=vm.label) xid = vm.start(verbose=options.verbose) - retcode = subprocess.call ([qrexec_daemon_path, str(xid)]) - if (retcode != 0) : - print "ERROR: Cannot start qrexec_daemon!" - exit (1) except (IOError, OSError, QubesException) as err: print "ERROR: {0}".format(err) @@ -96,39 +104,37 @@ def vm_run_cmd(vm, cmd, options): subprocess.call(["kdialog", "--error", "Not enough memory to start '{0}' VM! Close one or more running VMs and try again.".format(vm.name)]) exit (1) - if options.verbose: - print "--> Starting Qubes GUId..." + if os.getenv("DISPLAY") is not None: + if options.verbose: + print "--> Starting Qubes GUId..." - retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-e", cmd, "-i", vm.label.icon, "-l", str(vm.label.index)]) - if (retcode != 0) : - print "ERROR: Cannot start qubes_guid!" - if options.tray: - tray_notify_error ("ERROR: Cannot start qubes_guid!") - exit (1) - else: # VM already running... - guid_is_running = True - xid = vm.get_xid() - s = socket.socket (socket.AF_UNIX) - try: - s.connect ("/var/run/qubes/cmd_socket.{0}".format(xid)) - except (IOError, OSError) as e: - if e.errno in [errno.ENOENT,errno.ECONNREFUSED]: - guid_is_running = False - else: - print "ERROR: unix-connect: {0}".format(e) + retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon, "-l", str(vm.label.index)]) + if (retcode != 0) : + print "ERROR: Cannot start qubes_guid!" if options.tray: - tray_notify_error ("ERROR: Cannot connect to GUI daemon for this VM!") - exit(1) - if guid_is_running: - s.send (cmd) - s.close() - else: - retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-e", cmd, "-i", vm.label.icon, "-l", str(vm.label.index)]) + tray_notify_error ("ERROR: Cannot start qubes_guid!") + exit (1) + + if options.verbose: + print "--> Starting Qubes rexec daemon..." + + retcode = subprocess.call ([qrexec_daemon_path, str(xid)]) + if (retcode != 0) : + print "ERROR: Cannot start qrexec_daemon!" + exit (1) + + actually_execute(str(xid), cmd, options); + + else: # VM already running... + xid = vm.get_xid() + if os.getenv("DISPLAY") is not None and not os.path.isfile("/var/run/qubes/guid_running.{0}".format(xid)): + retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon, "-l", str(vm.label.index)]) if (retcode != 0) : print "ERROR: Cannot start qubes_guid!" if options.tray: tray_notify_error ("ERROR: Cannot start the GUI daemon for this VM!") exit (1) + actually_execute(str(xid), cmd, options); def main(): usage = "usage: %prog [options] [] []" @@ -159,6 +165,11 @@ def main(): parser.add_option ("--unpause", action="store_true", dest="unpause", default=False, help="Do 'xm unpause' for the VM(s) (can be combined this with --all and --wait)") + parser.add_option ("--pass_io", action="store_true", dest="passio", default=False, + help="Pass stdin/stdout/stderr from remote program") + + parser.add_option ("--localcmd", action="store", dest="localcmd", default=None, + help="With --pass_io, pass stdin/stdout/stderr to the given program") (options, args) = parser.parse_args () From eb7821771e38bfe0f2dfc7637302739e7441e3e1 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Mon, 7 Mar 2011 16:05:36 +0100 Subject: [PATCH 08/86] In qvm-start, check $DISPLAY existence, too. --- dom0/qvm-tools/qvm-start | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/dom0/qvm-tools/qvm-start b/dom0/qvm-tools/qvm-start index cd99f71e..f8f155fd 100755 --- a/dom0/qvm-tools/qvm-start +++ b/dom0/qvm-tools/qvm-start @@ -24,6 +24,7 @@ from qubes.qubes import QubesVmCollection from qubes.qubes import QubesException from optparse import OptionParser import subprocess +import os qubes_guid_path = "/usr/bin/qubes_guid" qrexec_daemon_path = "/usr/lib/qubes/qrexec_daemon" @@ -61,20 +62,22 @@ def main(): print "ERROR: {0}".format(err) exit (1) + if not options.noguid and os.getenv("DISPLAY") is not None: + if options.verbose: + print "--> Starting Qubes GUId..." + + retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon, "-l", str(vm.label.index)]) + if (retcode != 0) : + print "ERROR: Cannot start qubes_guid!" + exit (1) + + if options.verbose: + print "--> Starting Qubes rexec..." + retcode = subprocess.call ([qrexec_daemon_path, str(xid)]) if (retcode != 0) : print "ERROR: Cannot start qrexec_daemon!" exit (1) - if options.noguid: - exit (0) - if options.verbose: - print "--> Starting Qubes GUId..." - - retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon, "-l", str(vm.label.index)]) - if (retcode != 0) : - print "ERROR: Cannot start qubes_guid!" - exit (1) - main() From 0d12aeec8826c5344966555874c47349171f2f3d Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Mon, 7 Mar 2011 16:13:15 +0100 Subject: [PATCH 09/86] added "make -C qrexec clean" --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 01085b28..5601dcd7 100644 --- a/Makefile +++ b/Makefile @@ -27,3 +27,4 @@ clean: (cd dom0/restore && make clean) (cd dom0/qmemman && make clean) (cd common && make clean) + make -C qrexec clean From f263aa6b7c0d2cc34a55004eb0e05a568032c626 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 8 Mar 2011 12:24:47 +0100 Subject: [PATCH 10/86] Moved vchan and u2mfn code to core. --- Makefile | 1 + qrexec/Makefile | 2 +- rpm_spec/core-appvm.spec | 26 +++++ rpm_spec/core-dom0.spec | 7 ++ rpm_spec/core-netvm.spec | 7 ++ u2mfn/Makefile | 33 ++++++ u2mfn/u2mfn-kernel.h | 26 +++++ u2mfn/u2mfnlib.c | 75 +++++++++++++ u2mfn/u2mfnlib.h | 24 +++++ vchan/Makefile | 39 +++++++ vchan/init.c | 224 +++++++++++++++++++++++++++++++++++++++ vchan/io.c | 159 +++++++++++++++++++++++++++ vchan/libvchan.h | 60 +++++++++++ vchan/node-select.c | 133 +++++++++++++++++++++++ vchan/node.c | 157 +++++++++++++++++++++++++++ 15 files changed, 972 insertions(+), 1 deletion(-) create mode 100644 u2mfn/Makefile create mode 100644 u2mfn/u2mfn-kernel.h create mode 100644 u2mfn/u2mfnlib.c create mode 100644 u2mfn/u2mfnlib.h create mode 100644 vchan/Makefile create mode 100644 vchan/init.c create mode 100644 vchan/io.c create mode 100644 vchan/libvchan.h create mode 100644 vchan/node-select.c create mode 100644 vchan/node.c diff --git a/Makefile b/Makefile index 5601dcd7..bab9705b 100644 --- a/Makefile +++ b/Makefile @@ -28,3 +28,4 @@ clean: (cd dom0/qmemman && make clean) (cd common && make clean) make -C qrexec clean + make -C vchan clean diff --git a/qrexec/Makefile b/qrexec/Makefile index 72089fda..b87b09d6 100644 --- a/qrexec/Makefile +++ b/qrexec/Makefile @@ -1,5 +1,5 @@ CC=gcc -CFLAGS+=-g -Wall +CFLAGS+=-g -Wall -I../vchan XENLIBS=-lvchan -lxenstore -lxenctrl all: qrexec_daemon qrexec_agent qrexec_client diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index 6dccb5fb..8ca5daee 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -61,6 +61,8 @@ fi make clean all make -C ../common make -C ../qrexec +make -C ../vchan +make -C ../u2mfn %install @@ -98,6 +100,14 @@ cp xorg-preload-apps.conf $RPM_BUILD_ROOT/etc/X11 mkdir -p $RPM_BUILD_ROOT/home_volatile/user chown 500:500 $RPM_BUILD_ROOT/home_volatile/user +install -D ../vchan/libvchan.h $RPM_BUILD_ROOT/usr/include/libvchan.h +install -D ../u2mfn/u2mfnlib.h $RPM_BUILD_ROOT/usr/include/u2mfnlib.h +install -D ../u2mfn/u2mfn-kernel.h $RPM_BUILD_ROOT/usr/include/u2mfn-kernel.h + +install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so +install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so + + %triggerin -- initscripts cp /var/lib/qubes/serial.conf /etc/init/serial.conf @@ -214,3 +224,19 @@ rm -rf $RPM_BUILD_ROOT %dir /home_volatile %attr(700,user,user) /home_volatile/user /etc/X11/xorg-preload-apps.conf +/usr/include/libvchan.h +%{_libdir}/libvchan.so +%{_libdir}/libu2mfn.so + + +%package devel +Summary: Include files for qubes core libraries +License: GPL v2 only +Group: Development/Sources + +%description devel + +%files devel +/usr/include/libvchan.h +/usr/include/u2mfnlib.h +/usr/include/u2mfn-kernel.h diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 11182506..90163e28 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -50,6 +50,8 @@ python -O -m compileall qvm-core qmemman make -C restore make -C ../common make -C ../qrexec +make -C ../vchan +make -C ../u2mfn %install @@ -125,6 +127,9 @@ cp pm-utils/02qubes-pause-vms $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/ mkdir -p $RPM_BUILD_ROOT/var/log/qubes mkdir -p $RPM_BUILD_ROOT/var/run/qubes +install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so +install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so + %post /usr/lib/qubes/qubes_fix_nm_conf.sh @@ -284,3 +289,5 @@ fi %attr(4750,root,qubes) /usr/lib/qubes/xenfreepages %attr(2770,root,qubes) %dir /var/log/qubes %attr(770,root,qubes) %dir /var/run/qubes +%{_libdir}/libvchan.so +%{_libdir}/libu2mfn.so diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index 678da3a9..6c88fcac 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -51,6 +51,8 @@ fi %build make -C ../qrexec +make -C ../vchan +make -C ../u2mfn %install @@ -78,6 +80,9 @@ cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ mkdir -p $RPM_BUILD_ROOT/var/run/qubes mkdir -p $RPM_BUILD_ROOT/etc/xen/scripts cp ../common/vif-route-qubes $RPM_BUILD_ROOT/etc/xen/scripts +install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so +install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so + %triggerin -- initscripts cp /var/lib/qubes/serial.conf /etc/init/serial.conf @@ -186,3 +191,5 @@ rm -rf $RPM_BUILD_ROOT /sbin/qubes_serial_login /etc/xen/scripts/vif-route-qubes %dir /var/run/qubes +%{_libdir}/libvchan.so +%{_libdir}/libu2mfn.so diff --git a/u2mfn/Makefile b/u2mfn/Makefile new file mode 100644 index 00000000..9f08dcce --- /dev/null +++ b/u2mfn/Makefile @@ -0,0 +1,33 @@ +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Rafal Wojtczuk +# +# 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. +# +# + +CC=gcc +CFLAGS=-g -Wall +all: libu2mfn.so + +libu2mfn.so : u2mfnlib.o + gcc -shared -o libu2mfn.so u2mfnlib.o +u2mfnlib.o: u2mfnlib.c + gcc -fPIC -Wall -g -c u2mfnlib.c +clean: + rm -f *.o *so *~ libu2mfn.so + + diff --git a/u2mfn/u2mfn-kernel.h b/u2mfn/u2mfn-kernel.h new file mode 100644 index 00000000..ee244bc1 --- /dev/null +++ b/u2mfn/u2mfn-kernel.h @@ -0,0 +1,26 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ +#include + +#define U2MFN_MAGIC 0xf5 // See ioctl-number.txt in kernel docs + +#define U2MFN_GET_MFN_FOR_PAGE _IOW (U2MFN_MAGIC, 1, int) +#define U2MFN_GET_LAST_MFN _IO (U2MFN_MAGIC, 2) diff --git a/u2mfn/u2mfnlib.c b/u2mfn/u2mfnlib.c new file mode 100644 index 00000000..998f47e9 --- /dev/null +++ b/u2mfn/u2mfnlib.c @@ -0,0 +1,75 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include "u2mfn-kernel.h" + + +static int u2mfn_fd = -1; + +static int get_fd() +{ + if (u2mfn_fd == -1) { + u2mfn_fd = open("/proc/u2mfn", O_RDWR); + if (u2mfn_fd < 0) + return -1; + } + return 0; +} + +int u2mfn_get_mfn_for_page(long va, int *mfn) +{ + if (get_fd()) + return -1; + *mfn = ioctl(u2mfn_fd, U2MFN_GET_MFN_FOR_PAGE, va); + if (*mfn == -1) + return -1; + + return 0; +} + +int u2mfn_get_last_mfn(int *mfn) +{ + if (get_fd()) + return -1; + + *mfn = ioctl(u2mfn_fd, U2MFN_GET_LAST_MFN, 0); + if (*mfn == -1) + return -1; + + return 0; +} + + + +char *u2mfn_alloc_kpage() +{ + char *ret; + if (get_fd()) + return MAP_FAILED; + ret = + mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, u2mfn_fd, 0); + return ret; +} diff --git a/u2mfn/u2mfnlib.h b/u2mfn/u2mfnlib.h new file mode 100644 index 00000000..e64431af --- /dev/null +++ b/u2mfn/u2mfnlib.h @@ -0,0 +1,24 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +int u2mfn_get_mfn_for_page(long va, int *mfn) ; +int u2mfn_get_last_mfn(int *mfn) ; +char *u2mfn_alloc_kpage(void) ; diff --git a/vchan/Makefile b/vchan/Makefile new file mode 100644 index 00000000..ca7cc724 --- /dev/null +++ b/vchan/Makefile @@ -0,0 +1,39 @@ +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Rafal Wojtczuk +# +# 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. +# +# + +CC=gcc +CFLAGS=-g -Wall -I../u2mfn +all: libvchan.so + +libvchan.so : init.o io.o + gcc -shared -o libvchan.so init.o io.o -L ../u2mfn -lu2mfn +init.o: init.c + gcc -fPIC -Wall -g -c init.c +io.o: io.c + gcc -fPIC -Wall -g -c io.c +node: node.o libvchan.so + gcc -g -o node node.o -L. -lvchan -lxenctrl -lxenstore +node-select: node-select.o libvchan.so + gcc -g -o node-select node-select.o -L. -lvchan -lxenctrl -lxenstore +clean: + rm -f *.o *so *~ client server node node-select + + diff --git a/vchan/init.c b/vchan/init.c new file mode 100644 index 00000000..4a3da4f2 --- /dev/null +++ b/vchan/init.c @@ -0,0 +1,224 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libvchan.h" +#include "../u2mfn/u2mfnlib.h" + +static int ring_init(struct libvchan *ctrl) +{ + int u2mfn = open("/proc/u2mfn", O_RDONLY); + int mfn; + struct vchan_interface *ring; + ring = (struct vchan_interface *) u2mfn_alloc_kpage (); + + if (ring == MAP_FAILED) + return -1; + + ctrl->ring = ring; + if (u2mfn_get_last_mfn (&mfn) < 0) + return -1; + + ctrl->ring_ref = mfn; + close(u2mfn); + ring->cons_in = ring->prod_in = ring->cons_out = ring->prod_out = + 0; + ring->server_closed = ring->client_closed = 0; + ring->debug = 0xaabbccdd; + return 0; +} +/** + creates event channel; + creates "ring-ref" and "event-channel" xenstore entries; + waits for connection to event channel from the peer +*/ +static int server_interface_init(struct libvchan *ctrl, int devno) +{ + int ret = -1; + struct xs_handle *xs; + char buf[64]; + char ref[16]; + int evfd; + evtchn_port_or_error_t port; + xs = xs_domain_open(); + if (!xs) { + return ret; + } + evfd = xc_evtchn_open(); + if (evfd < 0) + goto fail; + ctrl->evfd = evfd; + // the following hardcoded 0 is the peer domain id + port = xc_evtchn_bind_unbound_port(evfd, 0); + if (port < 0) + goto fail2; + ctrl->evport = port; + snprintf(ref, sizeof ref, "%d", ctrl->ring_ref); + snprintf(buf, sizeof buf, "device/vchan/%d/ring-ref", devno); + if (!xs_write(xs, 0, buf, ref, strlen(ref))) + goto fail2; + snprintf(ref, sizeof ref, "%d", ctrl->evport); + snprintf(buf, sizeof buf, "device/vchan/%d/event-channel", devno); + if (!xs_write(xs, 0, buf, ref, strlen(ref))) + goto fail2; + // wait for the peer to arrive + if (xc_evtchn_pending(evfd) == -1) + goto fail2; + xc_evtchn_unmask(ctrl->evfd, ctrl->evport); + snprintf(buf, sizeof buf, "device/vchan/%d", devno); + xs_rm(xs, 0, buf); + + ret = 0; + fail2: + if (ret) + close(evfd); + fail: + xs_daemon_close(xs); + return ret; +} + +#define dir_select(dir1, dir2) \ + ctrl->wr_cons = &ctrl->ring->cons_##dir1; \ + ctrl->wr_prod = &ctrl->ring->prod_##dir1; \ + ctrl->rd_cons = &ctrl->ring->cons_##dir2; \ + ctrl->rd_prod = &ctrl->ring->prod_##dir2; \ + ctrl->wr_ring = ctrl->ring->buf_##dir1; \ + ctrl->rd_ring = ctrl->ring->buf_##dir2; \ + ctrl->wr_ring_size = sizeof(ctrl->ring->buf_##dir1); \ + ctrl->rd_ring_size = sizeof(ctrl->ring->buf_##dir2) + +/** + Run in AppVM (any domain). + Sleeps until the connection is established. + \param devno something like a well-known port. + \returns NULL on failure, handle on success +*/ +struct libvchan *libvchan_server_init(int devno) +{ + struct libvchan *ctrl = + (struct libvchan *) malloc(sizeof(struct libvchan)); + if (!ctrl) + return 0; + if (ring_init(ctrl)) + return 0;; + if (server_interface_init(ctrl, devno)) + return 0; +/* + We want the same code for read/write functions, regardless whether + we are client, or server. Thus, we do not access buf_in nor buf_out + buffers directly. Instead, in *_init functions, the dir_select + macro assigns proper values to wr* and rd* pointers, so that they + point to correct one out of buf_in or buf_out related fields. +*/ + dir_select(in, out); + ctrl->is_server = 1; + return ctrl; +} + +/** + retrieves ring-ref and event-channel numbers from xenstore (if + they don't exist, return error, because nobody seems to listen); + map the ring, connect the event channel +*/ +static int client_interface_init(struct libvchan *ctrl, int domain, int devno) +{ + int ret = -1; + unsigned int len; + struct xs_handle *xs; + int xcfd; + char buf[64]; + char *ref; + int evfd; + int remote_port; + xs = xs_daemon_open(); + if (!xs) { + return ret; + } + snprintf(buf, sizeof buf, + "/local/domain/%d/device/vchan/%d/ring-ref", domain, + devno); + ref = xs_read(xs, 0, buf, &len); + if (!ref) + goto fail; + ctrl->ring_ref = atoi(ref); + if (!ctrl->ring_ref) + goto fail; + free(ref); + snprintf(buf, sizeof buf, + "/local/domain/%d/device/vchan/%d/event-channel", domain, + devno); + ref = xs_read(xs, 0, buf, &len); + if (!ref) + goto fail; + remote_port = atoi(ref); + if (!remote_port) + goto fail; + free(ref); + xcfd = xc_interface_open(); + if (xcfd < 0) + goto fail; + ctrl->ring = (struct vchan_interface *) + xc_map_foreign_range(xcfd, domain, 4096, + PROT_READ | PROT_WRITE, ctrl->ring_ref); + close(xcfd); + if (ctrl->ring == 0 || ctrl->ring == MAP_FAILED) + goto fail; + evfd = xc_evtchn_open(); + if (evfd < 0) + goto fail; + ctrl->evfd = evfd; + ctrl->evport = + xc_evtchn_bind_interdomain(evfd, domain, remote_port); + if (ctrl->evport < 0 || xc_evtchn_notify(evfd, ctrl->evport)) + close(evfd); + else + ret = 0; + fail: + xs_daemon_close(xs); + return ret; +} + +/** + Run on the client side of connection (currently, must be dom0). + \returns NULL on failure (e.g. noone listening), handle on success +*/ +struct libvchan *libvchan_client_init(int domain, int devno) +{ + struct libvchan *ctrl = + (struct libvchan *) malloc(sizeof(struct libvchan)); + if (!ctrl) + return 0; + if (client_interface_init(ctrl, domain, devno)) + return 0; +// See comment in libvchan_server_init + dir_select(out, in); + ctrl->is_server = 0; + return ctrl; +} diff --git a/vchan/io.c b/vchan/io.c new file mode 100644 index 00000000..7b524279 --- /dev/null +++ b/vchan/io.c @@ -0,0 +1,159 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include "libvchan.h" +#include +#include +/** + \return How much data is immediately available for reading +*/ +int libvchan_data_ready(struct libvchan *ctrl) +{ + return *ctrl->rd_prod - *ctrl->rd_cons; +} + +/** + \return How much space is available for writing, without blocking +*/ +int libvchan_buffer_space(struct libvchan *ctrl) +{ + return ctrl->wr_ring_size - (*ctrl->wr_prod - *ctrl->wr_cons); +} + +static int do_notify(struct libvchan *ctrl) +{ + return xc_evtchn_notify(ctrl->evfd, ctrl->evport); +} + +/// returns nonzero if the peer has closed connection +int libvchan_is_eof(struct libvchan *ctrl) +{ + if (ctrl->is_server) { + if (ctrl->ring->client_closed) + return -1; + } else { + if (ctrl->ring->server_closed) { + ctrl->ring->client_closed = 1; + do_notify(ctrl); + return -1; + } + + } + return 0; +} + +/// waits for the peer to do any action +/** + \return -1 return value means peer has closed +*/ +int libvchan_wait(struct libvchan *ctrl) +{ + int ret; + ret = xc_evtchn_pending(ctrl->evfd); + if (ret!=-1 && xc_evtchn_unmask(ctrl->evfd, ctrl->evport)) + return -1; + if (ret!=-1 && libvchan_is_eof(ctrl)) + return -1; + return ret; +} + +/** + may sleep (only if no buffer space available); + may write less data than requested; + returns the amount of data processed, -1 on error or peer close +*/ +int libvchan_write(struct libvchan *ctrl, char *data, int size) +{ + int avail, avail_contig; + int real_idx; + while ((avail = libvchan_buffer_space(ctrl)) == 0) + if (libvchan_wait(ctrl) < 0) + return -1; + if (avail > size) + avail = size; + real_idx = (*ctrl->wr_prod) & (ctrl->wr_ring_size - 1); + avail_contig = ctrl->wr_ring_size - real_idx; + if (avail_contig < avail) + avail = avail_contig; + memcpy(ctrl->wr_ring + real_idx, data, avail); + *ctrl->wr_prod += avail; + if (do_notify(ctrl) < 0) + return -1; + return avail; +} + +/** + may sleep (only if no data is available for reading); + may return less data than requested; + returns the amount of data processed, -1 on error or peer close +*/ +int libvchan_read(struct libvchan *ctrl, char *data, int size) +{ + int avail, avail_contig; + int real_idx; + while ((avail = libvchan_data_ready(ctrl)) == 0) + if (libvchan_wait(ctrl) < 0) + return -1; + if (avail > size) + avail = size; + real_idx = (*ctrl->rd_cons) & (ctrl->rd_ring_size - 1); + avail_contig = ctrl->rd_ring_size - real_idx; + if (avail_contig < avail) + avail = avail_contig; + memcpy(data, ctrl->rd_ring + real_idx, avail); + *ctrl->rd_cons += avail; + if (do_notify(ctrl) < 0) + return -1; + return avail; +} + +/** + Wait fot the writes to finish, then notify the peer of closing + On server side, it waits for the peer to acknowledge +*/ +int libvchan_close(struct libvchan *ctrl) +{ + while (*ctrl->wr_prod != *ctrl->wr_cons) + if (libvchan_wait(ctrl) < 0) + return -1; + if (ctrl->is_server) { + ctrl->ring->server_closed = 1; + do_notify(ctrl); + while (!ctrl->ring->client_closed + && libvchan_wait(ctrl) == 0); + } else { + ctrl->ring->client_closed = 1; + do_notify(ctrl); + } + return 0; +} + +/// The fd to use for select() set +int libvchan_fd_for_select(struct libvchan *ctrl) +{ + return ctrl->evfd; +} + +/// Unmasks event channel; must be called before calling select(), and only then +void libvchan_prepare_to_select(struct libvchan *ctrl) +{ + xc_evtchn_unmask(ctrl->evfd, ctrl->evport); +} diff --git a/vchan/libvchan.h b/vchan/libvchan.h new file mode 100644 index 00000000..652284ba --- /dev/null +++ b/vchan/libvchan.h @@ -0,0 +1,60 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include +typedef uint32_t VCHAN_RING_IDX; + +/// struct vchan_interface is placed in memory shared between domains +struct vchan_interface { + // One buffer for each data direction + char buf_in[1024]; + char buf_out[2048]; + // standard consumer/producer interface, one pair per buffer + VCHAN_RING_IDX cons_in, prod_in, cons_out, prod_out; + uint32_t debug; + int client_closed, server_closed; +}; +/// struct libvchan is a control structure, passed to all library calls +struct libvchan { + struct vchan_interface *ring; + uint32_t ring_ref; + /// descriptor to event channel interface + int evfd; + int evport; + VCHAN_RING_IDX *wr_cons, *wr_prod, *rd_cons, *rd_prod; + char *rd_ring, *wr_ring; + int rd_ring_size, wr_ring_size; + int is_server; +}; + +struct libvchan *libvchan_server_init(int devno); + +struct libvchan *libvchan_client_init(int domain, int devno); + +int libvchan_write(struct libvchan *ctrl, char *data, int size); +int libvchan_read(struct libvchan *ctrl, char *data, int size); +int libvchan_wait(struct libvchan *ctrl); +int libvchan_close(struct libvchan *ctrl); +void libvchan_prepare_to_select(struct libvchan *ctrl); +int libvchan_fd_for_select(struct libvchan *ctrl); +int libvchan_is_eof(struct libvchan *ctrl); +int libvchan_data_ready(struct libvchan *ctrl); +int libvchan_buffer_space(struct libvchan *ctrl); diff --git a/vchan/node-select.c b/vchan/node-select.c new file mode 100644 index 00000000..a4f1cdaf --- /dev/null +++ b/vchan/node-select.c @@ -0,0 +1,133 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include "libvchan.h" +#include +#include +#include +#include +int libvchan_write_all(struct libvchan *ctrl, char *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = libvchan_write(ctrl, buf + written, size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } + return size; +} + +int write_all(int fd, char *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = write(fd, buf + written, size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } + return size; +} + + +void usage() +{ + fprintf(stderr, "usage:\n\tnode-select server nodeid\n" + "or\n" "\tnode-select client domainid nodeid\n"); + exit(1); +} + +#define BUFSIZE 5000 +char buf[BUFSIZE]; + +/** + Simple libvchan application, both client and server. + Both sides may write and read, both from the libvchan and from + stdin/stdout (just like netcat). More code is required to avoid + deadlock when both sides write, and noone reads. +*/ + +int main(int argc, char **argv) +{ + int ret; + int libvchan_fd; + struct libvchan *ctrl = 0; + if (argc < 3) + usage(); + if (!strcmp(argv[1], "server")) + ctrl = libvchan_server_init(atoi(argv[2])); + else if (!strcmp(argv[1], "client")) + ctrl = libvchan_client_init(atoi(argv[2]), atoi(argv[3])); + else + usage(); + if (!ctrl) { + perror("libvchan_*_init"); + exit(1); + } + + libvchan_fd = libvchan_fd_for_select(ctrl); + for (;;) { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(0, &rfds); + FD_SET(libvchan_fd, &rfds); +// libvchan_prepare_to_select(ctrl); + ret = select(libvchan_fd + 1, &rfds, NULL, NULL, NULL); + if (ret < 0) { + perror("select"); + exit(1); + } + if (libvchan_is_eof(ctrl)) + exit(0); + if (FD_ISSET(libvchan_fd, &rfds)) +// we don't care about the result, but we need to do the read to +// clear libvchan_fd pendind state + libvchan_wait(ctrl); + while (libvchan_data_ready(ctrl) > 0) { + ret = libvchan_read(ctrl, buf, BUFSIZE); + if (ret < 0) + exit(0); + write_all(1, buf, ret); + } + if (FD_ISSET(0, &rfds)) { + ret = read(0, buf, BUFSIZE); + if (ret == 0) { + libvchan_close(ctrl); + exit(0); + } + if (ret < 0) { + perror("read 0"); + exit(1); + } +// libvchan_write_all can block; so if both sides write a lot, +// we can deadlock. Need higher level solution; would libvchan_write be ok ? + libvchan_write_all(ctrl, buf, ret); + } + + } +} diff --git a/vchan/node.c b/vchan/node.c new file mode 100644 index 00000000..d739fe7f --- /dev/null +++ b/vchan/node.c @@ -0,0 +1,157 @@ +/* + * The Qubes OS Project, http://www.qubes-os.org + * + * Copyright (C) 2010 Rafal Wojtczuk + * + * 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. + * + */ + +#include "libvchan.h" +#include +#include +#include +#include +#include +int libvchan_write_all(struct libvchan *ctrl, char *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = libvchan_write(ctrl, buf + written, size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } + return size; +} + +int write_all(int fd, char *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = write(fd, buf + written, size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } + return size; +} + +void usage() +{ + fprintf(stderr, "usage:\n\tnode server [read|write] nodeid\n" + "or\n" "\tnode client [read|write] domainid nodeid\n"); + exit(1); +} + +#define BUFSIZE 5000 +char buf[BUFSIZE]; +void reader(struct libvchan *ctrl) +{ + int size; + for (;;) { + size = rand() % (BUFSIZE - 1) + 1; + size = libvchan_read(ctrl, buf, size); + fprintf(stderr, "#"); + if (size < 0) { + perror("read vchan"); + libvchan_close(ctrl); + exit(1); + } + if (size == 0) + break; + size = write_all(1, buf, size); + if (size < 0) { + perror("stdout write"); + exit(1); + } + if (size == 0) { + perror("write size=0?\n"); + exit(1); + } + } +} + +void writer(struct libvchan *ctrl) +{ + int size; + for (;;) { + size = rand() % (BUFSIZE - 1) + 1; + size = read(0, buf, size); + if (size < 0) { + perror("read stdin"); + libvchan_close(ctrl); + exit(1); + } + if (size == 0) + break; + size = libvchan_write_all(ctrl, buf, size); + fprintf(stderr, "#"); + if (size < 0) { + perror("vchan write"); + exit(1); + } + if (size == 0) { + perror("write size=0?\n"); + exit(1); + } + } +} + + +/** + Simple libvchan application, both client and server. + One side does writing, the other side does reading; both from + standard input/output fds. +*/ +int main(int argc, char **argv) +{ + int seed = time(0); + struct libvchan *ctrl = 0; + int wr; + if (argc < 4) + usage(); + if (!strcmp(argv[2], "read")) + wr = 0; + else if (!strcmp(argv[2], "write")) + wr = 1; + else + usage(); + if (!strcmp(argv[1], "server")) + ctrl = libvchan_server_init(atoi(argv[3])); + else if (!strcmp(argv[1], "client")) + ctrl = libvchan_client_init(atoi(argv[3]), atoi(argv[4])); + else + usage(); + if (!ctrl) { + perror("libvchan_*_init"); + exit(1); + } + + srand(seed); + fprintf(stderr, "seed=%d\n", seed); + if (wr) + writer(ctrl); + else + reader(ctrl); + libvchan_close(ctrl); + return 0; +} From a7cc09071f95b98d30cba1c7225238450f1ecf06 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 8 Mar 2011 13:03:55 +0100 Subject: [PATCH 11/86] Make qubes_restore rexec-aware. --- dom0/qvm-tools/qvm-start | 15 +++++++----- dom0/restore/qubes_prepare_saved_domain.sh | 2 +- dom0/restore/qubes_restore.c | 27 +++++++++++++++++++++- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/dom0/qvm-tools/qvm-start b/dom0/qvm-tools/qvm-start index f8f155fd..8a3d277e 100755 --- a/dom0/qvm-tools/qvm-start +++ b/dom0/qvm-tools/qvm-start @@ -35,6 +35,8 @@ def main(): parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True) parser.add_option ("--no-guid", action="store_true", dest="noguid", default=False, help="Do not start the GUId") + parser.add_option ("--no-rexec", action="store_true", dest="norexec", default=False, + help="Do not start rexec") parser.add_option ("--console", action="store_true", dest="debug_console", default=False, help="Attach debugging console to the newly started VM") parser.add_option ("--dvm", action="store_true", dest="preparing_dvm", default=False, @@ -71,13 +73,14 @@ def main(): print "ERROR: Cannot start qubes_guid!" exit (1) - if options.verbose: - print "--> Starting Qubes rexec..." + if not options.norexec: + if options.verbose: + print "--> Starting Qubes rexec..." - retcode = subprocess.call ([qrexec_daemon_path, str(xid)]) - if (retcode != 0) : - print "ERROR: Cannot start qrexec_daemon!" - exit (1) + retcode = subprocess.call ([qrexec_daemon_path, str(xid)]) + if (retcode != 0) : + print "ERROR: Cannot start qrexec_daemon!" + exit (1) main() diff --git a/dom0/restore/qubes_prepare_saved_domain.sh b/dom0/restore/qubes_prepare_saved_domain.sh index 1d1bf0c3..f427f59d 100755 --- a/dom0/restore/qubes_prepare_saved_domain.sh +++ b/dom0/restore/qubes_prepare_saved_domain.sh @@ -21,7 +21,7 @@ if ! [ -d $VMDIR ] ; then echo $VMDIR does not exist ? exit 1 fi -if ! qvm-start $1 --no-guid --dvm ; then +if ! qvm-start $1 --no-guid --no-rexec --dvm ; then exit 1 fi diff --git a/dom0/restore/qubes_restore.c b/dom0/restore/qubes_restore.c index 3788fb66..bafa55c4 100644 --- a/dom0/restore/qubes_restore.c +++ b/dom0/restore/qubes_restore.c @@ -182,6 +182,29 @@ int xend_connect() return s; } +void start_rexec(int domid) +{ + int pid, status; + char dstr[40]; + snprintf(dstr, sizeof(dstr), "%d", domid); + switch (pid = fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + execl("/usr/lib/qubes/qrexec_daemon", "qrexec_daemon", + dstr, NULL); + perror("execl"); + exit(1); + default:; + } + if (waitpid(pid, &status, 0) < 0) { + perror("waitpid"); + exit(1); + } +} + + void start_guid(int domid, int argc, char **argv) { int i; @@ -197,6 +220,7 @@ void start_guid(int domid, int argc, char **argv) execv("/usr/bin/qubes_guid", guid_args); perror("execv"); } + // modify the savefile. fd = fd to the open savefile, // buf - already read 1st page of the savefile // pattern - pattern to search for @@ -452,10 +476,11 @@ int main(int argc, char **argv) resp = recv_resp(fd); // printf("%s\n", resp); fprintf(stderr, "time=%s, creating xenstore entries\n", gettime()); -#endif +#endif setup_xenstore(netvm_id, domid, dispid, name); fprintf(stderr, "time=%s, starting qubes_guid\n", gettime()); rm_fast_flag(); + start_rexec(domid); start_guid(domid, argc, argv); return 0; } From 8f906236619fac16f891cc21764d1fb554bb2c97 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Thu, 10 Mar 2011 13:08:06 +0100 Subject: [PATCH 12/86] Add ability to execute command without help of /bin/su It is important, if the program closes stdout, but does not exit. Then, qrexec_agent does not see EOF (because su still holds the file descriptor). --- qrexec/qrexec_agent.c | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/qrexec/qrexec_agent.c b/qrexec/qrexec_agent.c index 1b6f7bad..8b1309c2 100644 --- a/qrexec/qrexec_agent.c +++ b/qrexec/qrexec_agent.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include "qrexec.h" #include "buffer.h" #include "glue.h" @@ -66,18 +68,46 @@ void init() peer_server_init(REXEC_PORT); } +void no_colon_in_cmd() +{ + fprintf(stderr, + "cmdline is supposed to be in user:command form\n"); + exit(1); +} + +void do_exec_directly(char *cmd) +{ + struct passwd *pwd; + char *sep = index(cmd, ':'); + if (!sep) + no_colon_in_cmd(); + *sep = 0; + pwd = getpwnam(cmd); + if (!pwd) { + perror("getpwnam"); + exit(1); + } + setgid(pwd->pw_gid); + initgroups(cmd, pwd->pw_gid); + setuid(pwd->pw_uid); + setenv("HOME", pwd->pw_dir, 1); + setenv("USER", cmd, 1); + execl(sep + 1, sep + 1, NULL); + perror("execl"); + exit(1); +} + void do_exec(char *cmd) { char *sep = index(cmd, ':'); - if (!sep) { - fprintf(stderr, - "cmdline is supposed to be in user:command form\n"); - exit(1); - } + if (!sep) + no_colon_in_cmd(); *sep = 0; signal(SIGCHLD, SIG_DFL); signal(SIGPIPE, SIG_DFL); + if (!strcmp(cmd, "directly")) + do_exec_directly(sep + 1); execl("/bin/su", "su", "-", cmd, "-c", sep + 1, NULL); perror("execl"); exit(1); @@ -89,7 +119,7 @@ void handle_just_exec(int clid, int len) int fdn, pid; read_all_vchan_ext(buf, len); - switch (pid=fork()) { + switch (pid = fork()) { case -1: perror("fork"); exit(1); From f1a7df6e9568909f15d17ddf562d0d18df5d1962 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Thu, 10 Mar 2011 15:41:31 +0100 Subject: [PATCH 13/86] Implemented mechanism to trigger predefined execution in dom0. Processes in AppVM can ask qrexec-agent to send a MSG_AGENT_TO_SERVER_TRIGGER_EXEC message to qrexec-daemon. The latter will execute predefined program. It is useful for the purpose of file copy; the predefined program will create a connected qfile-daemon<->qfile-agent pair. --- qrexec/qrexec.h | 8 +++++++ qrexec/qrexec_agent.c | 38 +++++++++++++++++++++++++++++++ qrexec/qrexec_daemon.c | 48 ++++++++++++++++++++++++++++++++++++++-- rpm_spec/core-appvm.spec | 2 ++ rpm_spec/core-netvm.spec | 2 ++ 5 files changed, 96 insertions(+), 2 deletions(-) diff --git a/qrexec/qrexec.h b/qrexec/qrexec.h index 729ff932..ec29ed51 100644 --- a/qrexec/qrexec.h +++ b/qrexec/qrexec.h @@ -25,6 +25,8 @@ #define REXEC_PORT 512 +#define QREXEC_AGENT_TRIGGER_PATH "/var/run/qubes/qrexec_agent" + enum { MSG_CLIENT_TO_SERVER_EXEC_CMDLINE = 0x100, MSG_CLIENT_TO_SERVER_JUST_EXEC, @@ -40,12 +42,18 @@ enum { MSG_AGENT_TO_SERVER_STDOUT, MSG_AGENT_TO_SERVER_STDERR, MSG_AGENT_TO_SERVER_EXIT_CODE, + MSG_AGENT_TO_SERVER_TRIGGER_EXEC, MSG_SERVER_TO_CLIENT_STDOUT, MSG_SERVER_TO_CLIENT_STDERR, MSG_SERVER_TO_CLIENT_EXIT_CODE }; +enum { + QREXEC_EXECUTE_FILE_COPY=0x700, + QREXEC_EXECUTE_FILE_COPY_FOR_DISPVM +}; + struct server_header { unsigned int type; unsigned int clid; diff --git a/qrexec/qrexec_agent.c b/qrexec/qrexec_agent.c index 8b1309c2..ef02ddb1 100644 --- a/qrexec/qrexec_agent.c +++ b/qrexec/qrexec_agent.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "qrexec.h" #include "buffer.h" #include "glue.h" @@ -63,9 +64,16 @@ struct _process_fd process_fd[MAX_FDS]; /* indexed by client id, which is descriptor number of a client in daemon */ struct _client_info client_info[MAX_FDS]; +int trigger_fd; + void init() { peer_server_init(REXEC_PORT); + umask(0); + mkfifo(QREXEC_AGENT_TRIGGER_PATH, 0666); + umask(077); + trigger_fd = + open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK); } void no_colon_in_cmd() @@ -418,6 +426,11 @@ int fill_fds_for_select(fd_set * rdset, fd_set * wrset) FD_SET(i, rdset); max = i; } + + FD_SET(trigger_fd, rdset); + if (trigger_fd > max) + max = trigger_fd; + for (i = 0; i < MAX_FDS; i++) if (client_info[i].pid > 0 && client_info[i].is_blocked) { fd = client_info[i].stdin_fd; @@ -446,6 +459,28 @@ void flush_client_data_agent(int clid) } } +void handle_trigger_io() +{ + struct server_header s_hdr; + char buf[5]; + + s_hdr.clid = 0; + s_hdr.len = 0; + if (read(trigger_fd, buf, 4) == 4) { + buf[4] = 0; + if (!strcmp(buf, "FCPR")) + s_hdr.clid = QREXEC_EXECUTE_FILE_COPY; + else if (!strcmp(buf, "DVMR")) + s_hdr.clid = QREXEC_EXECUTE_FILE_COPY_FOR_DISPVM; + if (s_hdr.clid) { + s_hdr.type = MSG_AGENT_TO_SERVER_TRIGGER_EXEC; + write_all_vchan_ext(&s_hdr, sizeof s_hdr); + } + } + close(trigger_fd); + trigger_fd = + open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK); +} int main() { @@ -469,6 +504,9 @@ int main() while (read_ready_vchan_ext()) handle_server_data(); + 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 > 0 diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index 5ceb4ab9..9f801983 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -51,9 +51,11 @@ int server_fd; void handle_usr1(int x) { - exit(0); + exit(0); } +char domain_id[64]; + void init(int xid) { char dbg_log[256]; @@ -63,6 +65,7 @@ void init(int xid) fprintf(stderr, "domain id=0?\n"); exit(1); } + snprintf(domain_id, sizeof(domain_id), "%d", xid); signal(SIGUSR1, handle_usr1); switch (fork()) { case -1: @@ -71,7 +74,7 @@ void init(int xid) case 0: break; default: - pause(); + pause(); exit(0); } close(0); @@ -94,6 +97,7 @@ void init(int xid) peer_client_init(xid, REXEC_PORT); setuid(getuid()); signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, SIG_IGN); signal(SIGUSR1, SIG_DFL); kill(getppid(), SIGUSR1); } @@ -242,6 +246,41 @@ void pass_to_client(int clid, struct client_header *hdr) } } +void handle_trigger_exec(int req) +{ + char *rcmd = NULL, *lcmd = NULL; + int i; + switch (req) { + case QREXEC_EXECUTE_FILE_COPY: + rcmd = "directly:user:/usr/lib/qubes/qfile-agent"; + lcmd = "/usr/lib/qubes/qfile-daemon"; + break; + case QREXEC_EXECUTE_FILE_COPY_FOR_DISPVM: + rcmd = "directly:user:/usr/lib/qubes/qfile-agent-dvm"; + lcmd = "/usr/lib/qubes/qfile-daemon-dvm"; + break; + default: + fprintf(stderr, "got trigger exec no %d\n", req); + exit(1); + } + switch (fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + break; + default: + return; + } + for (i = 3; i < 256; i++) + close(i); + signal(SIGCHLD, SIG_DFL); + execl("/usr/lib/qubes/qrexec_client", "qrexec_client", "-d", + domain_id, "-l", lcmd, rcmd, NULL); + perror("execl"); + exit(1); +} + void handle_agent_data() { struct client_header hdr; @@ -251,6 +290,11 @@ void handle_agent_data() // fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.clid, // s_hdr.len); + if (s_hdr.type == MSG_AGENT_TO_SERVER_TRIGGER_EXEC) { + handle_trigger_exec(s_hdr.clid); + return; + } + if (s_hdr.clid >= MAX_FDS || s_hdr.clid < 0) { fprintf(stderr, "from agent: clid=%d\n", s_hdr.clid); exit(1); diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index 8ca5daee..5d3038f3 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -107,6 +107,7 @@ install -D ../u2mfn/u2mfn-kernel.h $RPM_BUILD_ROOT/usr/include/u2mfn-kernel.h install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so +mkdir -p $RPM_BUILD_ROOT/var/run/qubes %triggerin -- initscripts cp /var/lib/qubes/serial.conf /etc/init/serial.conf @@ -227,6 +228,7 @@ rm -rf $RPM_BUILD_ROOT /usr/include/libvchan.h %{_libdir}/libvchan.so %{_libdir}/libu2mfn.so +%dir /var/run/qubes %package devel diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index 6c88fcac..84df5cbe 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -83,6 +83,7 @@ cp ../common/vif-route-qubes $RPM_BUILD_ROOT/etc/xen/scripts install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so +mkdir -p $RPM_BUILD_ROOT/var/run/qubes %triggerin -- initscripts cp /var/lib/qubes/serial.conf /etc/init/serial.conf @@ -193,3 +194,4 @@ rm -rf $RPM_BUILD_ROOT %dir /var/run/qubes %{_libdir}/libvchan.so %{_libdir}/libu2mfn.so +%dir /var/run/qubes From c2214e854c269e2f27a71f29b70ef84af8c8eff2 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Thu, 10 Mar 2011 16:50:40 +0100 Subject: [PATCH 14/86] Added dvm_file_editor. It works with qrexec - reads/writes data from stdin/stdout. --- appvm/Makefile | 6 +- appvm/dvm2.h | 1 + appvm/dvm_file_editor.c | 126 +++++++++++++++++++++++++++++++++++++++ rpm_spec/core-appvm.spec | 2 + 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 appvm/dvm2.h create mode 100644 appvm/dvm_file_editor.c diff --git a/appvm/Makefile b/appvm/Makefile index 6bb1dea6..e247ce9b 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,6 +1,8 @@ CC=gcc CFLAGS=-Wall -all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm +all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor +dvm_file_editor: dvm_file_editor.o + $(CC) -o dvm_file_editor dvm_file_editor.o qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o @@ -8,4 +10,4 @@ qubes_add_pendrive_script: qubes_add_pendrive_script.o qvm-open-in-dvm: qvm-open-in-dvm.o $(CC) -o qvm-open-in-dvm qvm-open-in-dvm.o -lxenstore clean: - rm -f qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm *.o *~ + rm -f dvm_file_editor qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm *.o *~ diff --git a/appvm/dvm2.h b/appvm/dvm2.h new file mode 100644 index 00000000..837999c2 --- /dev/null +++ b/appvm/dvm2.h @@ -0,0 +1 @@ +#define DVM_FILENAME_SIZE 256 diff --git a/appvm/dvm_file_editor.c b/appvm/dvm_file_editor.c new file mode 100644 index 00000000..cadb4509 --- /dev/null +++ b/appvm/dvm_file_editor.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include "dvm2.h" + +int write_all(int fd, void *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = write(fd, (char *) buf + written, size - written); + if (ret <= 0) { + perror("write"); + return 0; + } + written += ret; + } +// fprintf(stderr, "sent %d bytes\n", size); + return 1; +} + +int read_all(int fd, void *buf, int size) +{ + int got_read = 0; + int ret; + while (got_read < size) { + ret = read(fd, (char *) buf + got_read, size - got_read); + if (ret == 0) { + fprintf(stderr, "EOF\n"); + return 0; + } + if (ret < 0) { + perror("read"); + return 0; + } + got_read += ret; + } +// fprintf(stderr, "read %d bytes\n", size); + return 1; +} + +char *get_filename() +{ + char buf[DVM_FILENAME_SIZE]; + static char retname[sizeof(buf) + sizeof("/tmp/")]; + if (!read_all(0, buf, sizeof(buf))) + exit(1); + if (index(buf, '/')) { + fprintf(stderr, "filename contains /"); + exit(1); + } + snprintf(retname, sizeof(retname), "/tmp/%s", buf); + return retname; +} + +void copy_all(int fdout, int fdin) +{ + int ret; + char buf[4096]; + for (;;) { + ret = read(fdin, buf, sizeof(buf)); + if (!ret) + break; + if (ret < 0) { + perror("read"); + exit(1); + } + if (!write_all(fdout, buf, ret)) { + perror("write"); + exit(1); + } + } +} + + +void copy_file(char *filename) +{ + int fd = open(filename, O_WRONLY | O_CREAT, 0600); + if (fd < 0) { + perror("open file"); + exit(1); + } + copy_all(fd, 0); + close(fd); +} + +void send_file_back(char * filename) +{ + int fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("open file"); + exit(1); + } + copy_all(1, fd); + close(fd); +} + +int +main() +{ + char cmdbuf[512]; + struct stat stat_pre, stat_post; + char *filename = get_filename(); + + copy_file(filename); + if (stat(filename, &stat_pre)) { + perror("stat pre"); + exit(1); + } + snprintf(cmdbuf, sizeof(cmdbuf), + "HOME=/home/user DISPLAY=:0 /usr/bin/mimeopen -n -M '%s' 2>&1 > /tmp/kde-open.log Date: Fri, 11 Mar 2011 11:34:07 +0100 Subject: [PATCH 15/86] Added DVM_SPOOL definition to dvm2.h --- appvm/dvm2.h | 1 + 1 file changed, 1 insertion(+) diff --git a/appvm/dvm2.h b/appvm/dvm2.h index 837999c2..0e5922cd 100644 --- a/appvm/dvm2.h +++ b/appvm/dvm2.h @@ -1 +1,2 @@ #define DVM_FILENAME_SIZE 256 +#define DVM_SPOOL "/home/user/.dvmspool" From e19390ca1cefc49247b8a25945c8acc14f8af3bc Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 11 Mar 2011 11:47:20 +0100 Subject: [PATCH 16/86] Moved ioall.c file to "common" --- {qrexec => common}/ioall.c | 6 ------ common/ioall.h | 2 ++ qrexec/Makefile | 15 ++++++++------- qrexec/glue.h | 3 --- qrexec/qrexec_agent.c | 6 ++++++ qrexec/qrexec_client.c | 1 + qrexec/qrexec_daemon.c | 7 +++++++ 7 files changed, 24 insertions(+), 16 deletions(-) rename {qrexec => common}/ioall.c (93%) create mode 100644 common/ioall.h diff --git a/qrexec/ioall.c b/common/ioall.c similarity index 93% rename from qrexec/ioall.c rename to common/ioall.c index 9e8d7a3c..ce550c7e 100644 --- a/qrexec/ioall.c +++ b/common/ioall.c @@ -59,9 +59,3 @@ int read_all(int fd, void *buf, int size) // fprintf(stderr, "read %d bytes\n", size); return 1; } - -void set_nonblock(int fd) -{ - int fl = fcntl(fd, F_GETFL, 0); - fcntl(fd, F_SETFL, fl | O_NONBLOCK); -} diff --git a/common/ioall.h b/common/ioall.h new file mode 100644 index 00000000..1e76353e --- /dev/null +++ b/common/ioall.h @@ -0,0 +1,2 @@ +int write_all(int fd, void *buf, int size); +int read_all(int fd, void *buf, int size); diff --git a/qrexec/Makefile b/qrexec/Makefile index b87b09d6..d4a5c857 100644 --- a/qrexec/Makefile +++ b/qrexec/Makefile @@ -1,13 +1,14 @@ CC=gcc -CFLAGS+=-g -Wall -I../vchan +CFLAGS+=-g -Wall -I../vchan -I../common XENLIBS=-lvchan -lxenstore -lxenctrl +COMMONIOALL=../common/ioall.o all: qrexec_daemon qrexec_agent qrexec_client -qrexec_daemon: qrexec_daemon.o unix_server.o ioall.o txrx-vchan.o buffer.o write_stdin.o - $(CC) -g -o qrexec_daemon qrexec_daemon.o unix_server.o ioall.o txrx-vchan.o write_stdin.o buffer.o $(XENLIBS) -qrexec_agent: qrexec_agent.o ioall.o exec.o txrx-vchan.o write_stdin.o buffer.o - $(CC) -g -o qrexec_agent qrexec_agent.o ioall.o exec.o txrx-vchan.o write_stdin.o buffer.o $(XENLIBS) -qrexec_client: qrexec_client.o ioall.o exec.o - $(CC) -g -o qrexec_client qrexec_client.o ioall.o exec.o +qrexec_daemon: qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o buffer.o write_stdin.o + $(CC) -g -o qrexec_daemon qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o write_stdin.o buffer.o $(XENLIBS) +qrexec_agent: qrexec_agent.o exec.o txrx-vchan.o write_stdin.o buffer.o + $(CC) -g -o qrexec_agent qrexec_agent.o exec.o txrx-vchan.o write_stdin.o buffer.o $(XENLIBS) +qrexec_client: qrexec_client.o $(COMMONIOALL) exec.o + $(CC) -g -o qrexec_client qrexec_client.o $(COMMONIOALL) exec.o clean: rm -f *.o *~ qrexec_daemon qrexec_agent qrexec_client diff --git a/qrexec/glue.h b/qrexec/glue.h index 228b6866..abcad649 100644 --- a/qrexec/glue.h +++ b/qrexec/glue.h @@ -23,8 +23,6 @@ void do_fork_exec(char *cmdline, int *pid, int *stdin_fd, int *stdout_fd, int *stderr_fd); -int write_all(int fd, void *buf, int size); -int read_all(int fd, void *buf, int size); int peer_server_init(int port); char *peer_client_init(int dom, int port); void wait_for_vchan_or_argfd(int max, fd_set * rdset, fd_set * wrset); @@ -36,7 +34,6 @@ void fix_fds(int fdin, int fdout, int fderr); int get_server_socket(int domid); int do_accept(int s); -void set_nonblock(int fd); enum { WRITE_STDIN_OK = 0x200, diff --git a/qrexec/qrexec_agent.c b/qrexec/qrexec_agent.c index ef02ddb1..94cab149 100644 --- a/qrexec/qrexec_agent.c +++ b/qrexec/qrexec_agent.c @@ -142,6 +142,12 @@ void handle_just_exec(int clid, int len) fprintf(stderr, "executed (nowait) %s pid %d\n", buf, pid); } +void set_nonblock(int fd) +{ + int fl = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, fl | O_NONBLOCK); +} + void handle_exec(int clid, int len) { char buf[len]; diff --git a/qrexec/qrexec_client.c b/qrexec/qrexec_client.c index e2878480..38271cf8 100644 --- a/qrexec/qrexec_client.c +++ b/qrexec/qrexec_client.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "qrexec.h" #include "buffer.h" #include "glue.h" diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index 9f801983..54077185 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "qrexec.h" #include "buffer.h" #include "glue.h" @@ -145,6 +146,12 @@ void pass_to_agent(int fd, struct server_header *s_hdr) write_all_vchan_ext(buf, len); } +void set_nonblock(int fd) +{ + int fl = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, fl | O_NONBLOCK); +} + void handle_client_cmdline(int fd) { struct client_header hdr; From 19943f093c16f1a5076259abb8854cdf67c62420 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 11 Mar 2011 11:50:52 +0100 Subject: [PATCH 17/86] Make dvm_file_editor use ioall.c --- appvm/Makefile | 6 +++--- appvm/dvm_file_editor.c | 37 +------------------------------------ 2 files changed, 4 insertions(+), 39 deletions(-) diff --git a/appvm/Makefile b/appvm/Makefile index e247ce9b..be08e07b 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,8 +1,8 @@ CC=gcc -CFLAGS=-Wall +CFLAGS=-Wall -I../common all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor -dvm_file_editor: dvm_file_editor.o - $(CC) -o dvm_file_editor dvm_file_editor.o +dvm_file_editor: dvm_file_editor.o ../common/ioall.o + $(CC) -o dvm_file_editor dvm_file_editor.o ../common/ioall.o qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o diff --git a/appvm/dvm_file_editor.c b/appvm/dvm_file_editor.c index cadb4509..fd60ca40 100644 --- a/appvm/dvm_file_editor.c +++ b/appvm/dvm_file_editor.c @@ -4,44 +4,9 @@ #include #include #include +#include #include "dvm2.h" -int write_all(int fd, void *buf, int size) -{ - int written = 0; - int ret; - while (written < size) { - ret = write(fd, (char *) buf + written, size - written); - if (ret <= 0) { - perror("write"); - return 0; - } - written += ret; - } -// fprintf(stderr, "sent %d bytes\n", size); - return 1; -} - -int read_all(int fd, void *buf, int size) -{ - int got_read = 0; - int ret; - while (got_read < size) { - ret = read(fd, (char *) buf + got_read, size - got_read); - if (ret == 0) { - fprintf(stderr, "EOF\n"); - return 0; - } - if (ret < 0) { - perror("read"); - return 0; - } - got_read += ret; - } -// fprintf(stderr, "read %d bytes\n", size); - return 1; -} - char *get_filename() { char buf[DVM_FILENAME_SIZE]; From 64bce77ef7840a2b485598f03b2e60155df6440b Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 11 Mar 2011 11:54:39 +0100 Subject: [PATCH 18/86] Changed copy_all signature. --- appvm/dvm_file_editor.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/appvm/dvm_file_editor.c b/appvm/dvm_file_editor.c index fd60ca40..3cc8818f 100644 --- a/appvm/dvm_file_editor.c +++ b/appvm/dvm_file_editor.c @@ -21,7 +21,7 @@ char *get_filename() return retname; } -void copy_all(int fdout, int fdin) +int copy_fd_all(int fdout, int fdin) { int ret; char buf[4096]; @@ -31,13 +31,14 @@ void copy_all(int fdout, int fdin) break; if (ret < 0) { perror("read"); - exit(1); + return 0; } if (!write_all(fdout, buf, ret)) { perror("write"); - exit(1); + return 0; } } + return 1; } @@ -48,7 +49,8 @@ void copy_file(char *filename) perror("open file"); exit(1); } - copy_all(fd, 0); + if (!copy_fd_all(fd, 0)) + exit(1); close(fd); } @@ -59,7 +61,8 @@ void send_file_back(char * filename) perror("open file"); exit(1); } - copy_all(1, fd); + if (!copy_fd_all(1, fd)) + exit(1); close(fd); } From bd89fa06311c603451ca31f8c2554225b387d8be Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 11 Mar 2011 11:57:16 +0100 Subject: [PATCH 19/86] Move copy_all_fd from dvm_file_editor.c to ioall.c It is useful in e.g. qfile-agent-dvm. --- appvm/dvm_file_editor.c | 21 --------------------- common/ioall.c | 21 +++++++++++++++++++++ common/ioall.h | 1 + 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/appvm/dvm_file_editor.c b/appvm/dvm_file_editor.c index 3cc8818f..4eed30ba 100644 --- a/appvm/dvm_file_editor.c +++ b/appvm/dvm_file_editor.c @@ -21,27 +21,6 @@ char *get_filename() return retname; } -int copy_fd_all(int fdout, int fdin) -{ - int ret; - char buf[4096]; - for (;;) { - ret = read(fdin, buf, sizeof(buf)); - if (!ret) - break; - if (ret < 0) { - perror("read"); - return 0; - } - if (!write_all(fdout, buf, ret)) { - perror("write"); - return 0; - } - } - return 1; -} - - void copy_file(char *filename) { int fd = open(filename, O_WRONLY | O_CREAT, 0600); diff --git a/common/ioall.c b/common/ioall.c index ce550c7e..1fca6f12 100644 --- a/common/ioall.c +++ b/common/ioall.c @@ -59,3 +59,24 @@ int read_all(int fd, void *buf, int size) // fprintf(stderr, "read %d bytes\n", size); return 1; } + +int copy_fd_all(int fdout, int fdin) +{ + int ret; + char buf[4096]; + for (;;) { + ret = read(fdin, buf, sizeof(buf)); + if (!ret) + break; + if (ret < 0) { + perror("read"); + return 0; + } + if (!write_all(fdout, buf, ret)) { + perror("write"); + return 0; + } + } + return 1; +} + diff --git a/common/ioall.h b/common/ioall.h index 1e76353e..1a700c6c 100644 --- a/common/ioall.h +++ b/common/ioall.h @@ -1,2 +1,3 @@ int write_all(int fd, void *buf, int size); int read_all(int fd, void *buf, int size); +int copy_fd_all(int fdout, int fdin); From 04da9b62a70eb9af7f3f6f8a4805761ef2159a19 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 11 Mar 2011 12:44:19 +0100 Subject: [PATCH 20/86] Added qfile-agent-dvm.c Nations, rejoice. --- appvm/Makefile | 4 +- appvm/qfile-agent-dvm.c | 156 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 appvm/qfile-agent-dvm.c diff --git a/appvm/Makefile b/appvm/Makefile index be08e07b..d6bfb6a2 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,8 +1,10 @@ CC=gcc CFLAGS=-Wall -I../common -all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor +all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm dvm_file_editor: dvm_file_editor.o ../common/ioall.o $(CC) -o dvm_file_editor dvm_file_editor.o ../common/ioall.o +qfile-agent-dvm: qfile-agent-dvm.o ../common/ioall.o + $(CC) -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o diff --git a/appvm/qfile-agent-dvm.c b/appvm/qfile-agent-dvm.c new file mode 100644 index 00000000..194d67cb --- /dev/null +++ b/appvm/qfile-agent-dvm.c @@ -0,0 +1,156 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dvm2.h" + +//the below usage of "system" is smelly, but should be fine - input comes from the same vm +void fatal(char *msg) +{ + char *errcmd; + asprintf(&errcmd, "DISPLAY=:0 kdialog --sorry 'qfile-agent-dvm: %s'", msg); + system(errcmd); + perror(msg); + exit(1); +} + +void nonfatal(char *msg) +{ + char *errcmd; + asprintf(&errcmd, "DISPLAY=:0 kdialog --sorry 'qfile-agent-dvm: %s'", msg); + system(errcmd); + fprintf(stderr, "%s", msg); +} + +void send_file(char *fname) +{ + char *base; + int fd = open(fname, O_RDONLY); + if (fd < 0) + fatal("open file_to_be_edited"); + base = rindex(fname, '/'); + if (!base) + base = fname; + else + base++; + if (strlen(base) >= DVM_FILENAME_SIZE) + base += strlen(base) - DVM_FILENAME_SIZE + 1; + if (!write_all(1, base, DVM_FILENAME_SIZE)) + fatal("send filename"); + if (!copy_fd_all(1, fd)) + fatal("send file"); + close(1); +} + +int copy_and_return_nonemptiness(int tmpfd) +{ + struct stat st; + if (!copy_fd_all(tmpfd, 0)) + fatal("receiving file"); + if (fstat(tmpfd, &st)) + fatal("fstat"); + close(tmpfd); + + return st.st_size; +} + +void recv_file_nowrite(char *fname) +{ + char *tempfile; + char *errmsg; + int tmpfd; + + asprintf(&tempfile, "/tmp/file_edited_in_dvm.XXXXXX"); + tmpfd = mkstemp(tempfile); + if (tmpfd < 0) + fatal("unable to create any temporary file, aborting"); + if (!copy_and_return_nonemptiness(tmpfd)) { + unlink(tempfile); + return; + } + asprintf(&errmsg, + "The file %s has been edited in Disposable VM and the modified content has been received, " + "but this file is in nonwritable directory and thus cannot be modified safely. The edited file has been " + "saved to %s", fname, tempfile); + nonfatal(errmsg); +} + +void actually_recv_file(char *fname, char *tempfile, int tmpfd) +{ + if (!copy_and_return_nonemptiness(tmpfd)) { + unlink(tempfile); + return; + } + if (rename(tempfile, fname)) + fatal("rename"); +} + +void recv_file(char *fname) +{ + int tmpfd; + char *tempfile; + asprintf(&tempfile, "%s.XXXXXX", fname); + tmpfd = mkstemp(tempfile); + if (tmpfd < 0) + recv_file_nowrite(fname); + else + actually_recv_file(fname, tempfile, tmpfd); +} + +void talk_to_daemon(char *fname) +{ + send_file(fname); + recv_file(fname); +} + +void process_spoolentry(char *entry_name) +{ + char *abs_spool_entry_name; + int entry_fd; + struct stat st; + char *filename; + int entry_size; + asprintf(&abs_spool_entry_name, "%s/%s", DVM_SPOOL, entry_name); + entry_fd = open(abs_spool_entry_name, O_RDONLY); + unlink(abs_spool_entry_name); + if (entry_fd < 0 || fstat(entry_fd, &st)) + fatal("bad dvm_entry"); + entry_size = st.st_size; + filename = calloc(1, entry_size + DVM_FILENAME_SIZE); + if (!filename) + fatal("malloc"); + if (!read_all(entry_fd, filename, entry_size)) + fatal("read dvm entry"); + close(entry_fd); + talk_to_daemon(filename); +} + +void scan_spool(char *name) +{ + struct dirent *ent; + DIR *dir = opendir(name); + if (!dir) + fatal("opendir"); + while ((ent = readdir(dir))) { + char *fname = ent->d_name; + if (!strcmp(fname, ".") || !strcmp(fname, "..")) + continue; + process_spoolentry(fname); + break; + } + closedir(dir); +} + +int main() +{ + signal(SIGPIPE, SIG_IGN); + scan_spool(DVM_SPOOL); + return 0; +} From 1a5bfd8c2b0a2510d6fc4b8b9055e605e6879a1e Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 11 Mar 2011 13:08:19 +0100 Subject: [PATCH 21/86] Reset SIGPIPE in qrexec_daemon, too. --- qrexec/qrexec_daemon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index 54077185..906905e4 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -282,6 +282,7 @@ void handle_trigger_exec(int req) for (i = 3; i < 256; i++) close(i); signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); execl("/usr/lib/qubes/qrexec_client", "qrexec_client", "-d", domain_id, "-l", lcmd, rcmd, NULL); perror("execl"); From b9e0e93a9072a360c4d7f905c3c0f8804f6d3953 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 11 Mar 2011 13:16:33 +0100 Subject: [PATCH 22/86] In qrexec_client, check write_all(local_stdin_fd,..) value --- qrexec/qrexec_client.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qrexec/qrexec_client.c b/qrexec/qrexec_client.c index 38271cf8..db4ba607 100644 --- a/qrexec/qrexec_client.c +++ b/qrexec/qrexec_client.c @@ -125,8 +125,10 @@ void handle_daemon_data(int s) case MSG_SERVER_TO_CLIENT_STDOUT: if (hdr.len == 0) close(local_stdin_fd); - else - write_all(local_stdin_fd, buf, hdr.len); + else if (!write_all(local_stdin_fd, buf, hdr.len)) { + perror("write local stdout"); + exit(1); + } break; case MSG_SERVER_TO_CLIENT_STDERR: write_all(2, buf, hdr.len); From 470ddce435f04b56005a31fc6a1b1813415c1260 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 11 Mar 2011 14:14:04 +0100 Subject: [PATCH 23/86] qrexec_daemon creates VMname-based link to its socket --- qrexec/glue.h | 2 +- qrexec/qrexec_daemon.c | 13 ++++++------- qrexec/unix_server.c | 8 +++++++- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/qrexec/glue.h b/qrexec/glue.h index abcad649..81c6c9e5 100644 --- a/qrexec/glue.h +++ b/qrexec/glue.h @@ -32,7 +32,7 @@ int write_all_vchan_ext(void *buf, int size); int buffer_space_vchan_ext(); void fix_fds(int fdin, int fdout, int fderr); -int get_server_socket(int domid); +int get_server_socket(int domid, char * domname); int do_accept(int s); enum { diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index 906905e4..c955dbd2 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -55,7 +55,7 @@ void handle_usr1(int x) exit(0); } -char domain_id[64]; +char *remote_domain_name; void init(int xid) { @@ -66,7 +66,6 @@ void init(int xid) fprintf(stderr, "domain id=0?\n"); exit(1); } - snprintf(domain_id, sizeof(domain_id), "%d", xid); signal(SIGUSR1, handle_usr1); switch (fork()) { case -1: @@ -83,6 +82,8 @@ void init(int xid) "/var/log/qubes/qrexec.%d.log", xid); umask(0007); logfd = open(dbg_log, O_WRONLY | O_CREAT | O_TRUNC, 0640); + umask(0077); + dup2(logfd, 1); dup2(logfd, 2); @@ -92,11 +93,9 @@ void init(int xid) exit(1); } - umask(0); - server_fd = get_server_socket(xid); - umask(0077); - peer_client_init(xid, REXEC_PORT); + remote_domain_name = peer_client_init(xid, REXEC_PORT); setuid(getuid()); + server_fd = get_server_socket(xid, remote_domain_name); signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGUSR1, SIG_DFL); @@ -284,7 +283,7 @@ void handle_trigger_exec(int req) signal(SIGCHLD, SIG_DFL); signal(SIGPIPE, SIG_DFL); execl("/usr/lib/qubes/qrexec_client", "qrexec_client", "-d", - domain_id, "-l", lcmd, rcmd, NULL); + remote_domain_name, "-l", lcmd, rcmd, NULL); perror("execl"); exit(1); } diff --git a/qrexec/unix_server.c b/qrexec/unix_server.c index aedf9167..14a61273 100644 --- a/qrexec/unix_server.c +++ b/qrexec/unix_server.c @@ -27,15 +27,21 @@ #include #include "qrexec.h" -int get_server_socket(int domid) +int get_server_socket(int domid, char *domname) { struct sockaddr_un sockname; int s; char socket_address[40]; + char link_to_socket_name[strlen(domname) + sizeof(socket_address)]; snprintf(socket_address, sizeof(socket_address), QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", domid); + snprintf(link_to_socket_name, sizeof link_to_socket_name, + QREXEC_DAEMON_SOCKET_DIR "/qrexec.%s", domname); unlink(socket_address); + unlink(link_to_socket_name); + symlink(socket_address, link_to_socket_name); + s = socket(AF_UNIX, SOCK_STREAM, 0); memset(&sockname, 0, sizeof(sockname)); sockname.sun_family = AF_UNIX; From 00f4bf119789f15847f2204e92e18c5ad030ae74 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 11 Mar 2011 16:06:00 +0100 Subject: [PATCH 24/86] qrexec_client accepts non-numeric domain description. Just tries to open qrexec.argv[1]. --- qrexec/qrexec_client.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/qrexec/qrexec_client.c b/qrexec/qrexec_client.c index db4ba607..23a4dac1 100644 --- a/qrexec/qrexec_client.c +++ b/qrexec/qrexec_client.c @@ -30,7 +30,7 @@ #include "buffer.h" #include "glue.h" -int connect_unix_socket(int domid) +int connect_unix_socket(char *domname) { int s, len; struct sockaddr_un remote; @@ -42,7 +42,7 @@ int connect_unix_socket(int domid) remote.sun_family = AF_UNIX; snprintf(remote.sun_path, sizeof remote.sun_path, - QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", domid); + QREXEC_DAEMON_SOCKET_DIR "/qrexec.%s", domname); len = strlen(remote.sun_path) + sizeof(remote.sun_family); if (connect(s, (struct sockaddr *) &remote, len) == -1) { perror("connect"); @@ -204,14 +204,14 @@ void usage(char *name) int main(int argc, char **argv) { int opt; - int domid = 0; + char *domname = NULL; int s; int just_exec = 0; char *local_cmdline = NULL; while ((opt = getopt(argc, argv, "d:l:e")) != -1) { switch (opt) { case 'd': - domid = atoi(optarg); + domname = strdup(optarg); break; case 'l': local_cmdline = strdup(optarg); @@ -223,10 +223,13 @@ int main(int argc, char **argv) usage(argv[0]); } } - if (optind >= argc || !domid) + if (optind >= argc || !domname) usage(argv[0]); - s = connect_unix_socket(domid); + + s = connect_unix_socket(domname); + setenv("QREXEC_REMOTE_DOMAIN", domname, 1); prepare_local_fds(local_cmdline); + if (just_exec) send_cmdline(s, MSG_CLIENT_TO_SERVER_JUST_EXEC, argv[optind]); From 5d3c43e4fabb4e18ee08e6eb04096f95b983ba09 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Mon, 14 Mar 2011 10:43:09 +0100 Subject: [PATCH 25/86] created qfile-daemon-dvm Mostly code from qfilexchgd; it will be removed soon. --- dom0/restore/qfile-daemon-dvm | 133 ++++++++++++++++++++++++++++++++++ rpm_spec/core-dom0.spec | 2 + 2 files changed, 135 insertions(+) create mode 100755 dom0/restore/qfile-daemon-dvm diff --git a/dom0/restore/qfile-daemon-dvm b/dom0/restore/qfile-daemon-dvm new file mode 100755 index 00000000..886d858b --- /dev/null +++ b/dom0/restore/qfile-daemon-dvm @@ -0,0 +1,133 @@ +#!/usr/bin/python2.6 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Rafal Wojtczuk +# +# 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. +# +# +import os +import dbus +import subprocess +import sys + +from qubes.qubes import QubesVmCollection +from qubes.qubes import QubesException +from qubes.qubes import QubesDaemonPidfile +from qubes.qmemman_client import QMemmanClient + +current_savefile = '/var/run/qubes/current_savefile' +notify_object = None + +class QfileDaemonDvm: + def __init__(self, name): + self.name = name + + def do_get_dvm(self): + qmemman_client = QMemmanClient() + if not qmemman_client.request_memory(400*1024*1024): + qmemman_client.close() + errmsg = 'Not enough memory to create DVM. ' + errmsg +='Terminate some appVM and retry.' + subprocess.call(['/usr/bin/kdialog', '--sorry', errmsg]) + return None + + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_writing() + qvm_collection.load() + + vm = qvm_collection.get_vm_by_name(self.name) + if vm is None: + sys.stderr.write( 'Domain ' + vmname + ' does not exist ?') + qvm_collection.unlock_db() + qmemman_client.close() + return None + retcode = subprocess.call(['/usr/lib/qubes/qubes_restore', + current_savefile, + '-c', vm.label.color, + '-i', vm.label.icon, + '-l', str(vm.label.index)]) + qmemman_client.close() + if retcode != 0: + subprocess.call(['/usr/bin/kdialog', '--sorry', 'DisposableVM creation failed, see qubes_restore.log']) + qvm_collection.unlock_db() + return None + f = open('/var/run/qubes/dispVM_xid', 'r'); + disp_xid = f.readline().rstrip('\n') + disp_name = f.readline().rstrip('\n') + disptempl = f.readline().rstrip('\n') + f.close() + vm_disptempl = qvm_collection.get_vm_by_name(disptempl); + if vm_disptempl is None: + sys.stderr.write( 'Domain ' + disptempl + ' does not exist ?') + qvm_collection.unlock_db() + return None + qvm_collection.add_new_disposablevm(disp_name, vm_disptempl.template_vm, label=vm.label) + qvm_collection.save() + qvm_collection.unlock_db() + + return disp_name + + def dvm_setup_ok(self): + dvmdata_dir = '/var/lib/qubes/dvmdata/' + if not os.path.isfile(current_savefile): + return False + if not os.path.isfile(dvmdata_dir+'default_savefile') or not os.path.isfile(dvmdata_dir+'savefile_root'): + return False + dvm_mtime = os.stat(current_savefile).st_mtime + root_mtime = os.stat(dvmdata_dir+'savefile_root').st_mtime + if dvm_mtime < root_mtime: + return False + return True + + def tray_notify(self, str, timeout = 3000): + notify_object.Notify("Qubes", 0, "red", "Qubes", str, [], [], timeout, dbus_interface="org.freedesktop.Notifications") + + def tray_notify_error(self, str, timeout = 3000): + notify_object.Notify("Qubes", 0, "dialog-error", "Qubes", str, [], [], timeout, dbus_interface="org.freedesktop.Notifications") + + def get_dvm(self): + if not self.dvm_setup_ok(): + self.tray_notify("Updating DisposableVM savefile, please wait") + if os.system("qvm-create-default-dvm --default-template --default-script >/var/run/qubes/qvm-create-default-dvm.stdout Date: Mon, 14 Mar 2011 11:25:18 +0100 Subject: [PATCH 26/86] Added new qvm-open-in-dvm, aka qvm-open-in-dvm2 Small, childless bash script. --- appvm/qvm-dvm.desktop | 2 +- appvm/qvm-open-in-dvm2 | 40 ++++++++++++++++++++++++++++++++++++++++ rpm_spec/core-appvm.spec | 3 ++- 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100755 appvm/qvm-open-in-dvm2 diff --git a/appvm/qvm-dvm.desktop b/appvm/qvm-dvm.desktop index a7f5ad77..67f9ea51 100644 --- a/appvm/qvm-dvm.desktop +++ b/appvm/qvm-dvm.desktop @@ -4,7 +4,7 @@ Type=Service X-KDE-ServiceTypes=KonqPopupMenu/Plugin,all/allfiles [Desktop Action QvmDvm] -Exec=/usr/bin/qvm-open-in-dvm disposable %U +Exec=/usr/bin/qvm-open-in-dvm2 %U Icon=kget Name=Open In DisposableVM diff --git a/appvm/qvm-open-in-dvm2 b/appvm/qvm-open-in-dvm2 new file mode 100755 index 00000000..eb0d4e2e --- /dev/null +++ b/appvm/qvm-open-in-dvm2 @@ -0,0 +1,40 @@ +#!/bin/bash +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Rafal Wojtczuk +# +# 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. +# +# + +if ! [ $# = 1 ] ; then + echo "Usage: $0 filename" + exit 1 +fi + +FILE="$1" +if ! [ "X""${FILE:0:1}" = X/ ] ; then + FILE="$PWD"/"$1" +fi + +DVMSPOOL=/home/user/.dvmspool +if ! [ -e $DVMSPOOL ] ; then + mkdir $DVMSPOOL || exit 1 +fi + +echo -n "$FILE" > $DVMSPOOL/req.$$ +echo -n DVMR > /var/run/qubes/qrexec_agent + diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index f7cc7311..b947b83b 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -72,7 +72,7 @@ mkdir -p $RPM_BUILD_ROOT/etc/init.d cp qubes_core $RPM_BUILD_ROOT/etc/init.d/ mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/usr/bin -cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm $RPM_BUILD_ROOT/usr/bin +cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm qvm-open-in-dvm2 $RPM_BUILD_ROOT/usr/bin mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes @@ -207,6 +207,7 @@ rm -rf $RPM_BUILD_ROOT /usr/bin/qvm-copy-to-vm /usr/lib/qubes/qvm-copy-to-vm.kde %attr(4755,root,root) /usr/bin/qvm-open-in-dvm +/usr/bin/qvm-open-in-dvm2 /usr/lib/qubes/qvm-dvm-transfer /usr/lib/qubes/meminfo-writer /usr/lib/qubes/dvm_file_editor From d82001819dd92030579556d50518a5b676360ac9 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Mon, 14 Mar 2011 20:57:08 +0100 Subject: [PATCH 27/86] Properly call QubesProxyVm superclass --- dom0/qvm-core/qubes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 2ac48904..176673dd 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1116,7 +1116,7 @@ class QubesProxyVm(QubesNetVm): def start(self, debug_console = False, verbose = False, preparing_dvm = False): if dry_run: return - retcode = super(QubesFirewallVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) + retcode = super(QubesProxyVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) self.netvm_vm.add_external_ip_permission(self.get_xid()) self.write_netvm_domid_entry() return retcode @@ -1125,7 +1125,7 @@ class QubesProxyVm(QubesNetVm): if dry_run: return self.netvm_vm.remove_external_ip_permission(self.get_xid()) - super(QubesFirewallVm, self).force_shutdown() + super(QubesProxyVm, self).force_shutdown() def create_xenstore_entries(self, xid): if dry_run: From c0ca1a9f50ab1d8a1d549526306f0a8a7ae9405b Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 15 Mar 2011 10:36:50 +0100 Subject: [PATCH 28/86] Make sure read_all sets errno to 0 at EOF. --- common/ioall.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/ioall.c b/common/ioall.c index 1fca6f12..413477fc 100644 --- a/common/ioall.c +++ b/common/ioall.c @@ -23,6 +23,7 @@ #include #include #include +#include int write_all(int fd, void *buf, int size) { @@ -47,6 +48,7 @@ int read_all(int fd, void *buf, int size) while (got_read < size) { ret = read(fd, (char *) buf + got_read, size - got_read); if (ret == 0) { + errno = 0; fprintf(stderr, "EOF\n"); return 0; } From 0ed004904c82c5110d0e3fa92fdc15652d9d7676 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 15 Mar 2011 13:00:12 +0100 Subject: [PATCH 29/86] Handy gui_fatal() etc routines. --- common/gui-fatal.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++ common/gui-fatal.h | 2 ++ 2 files changed, 52 insertions(+) create mode 100644 common/gui-fatal.c create mode 100644 common/gui-fatal.h diff --git a/common/gui-fatal.c b/common/gui-fatal.c new file mode 100644 index 00000000..ed2b3d47 --- /dev/null +++ b/common/gui-fatal.c @@ -0,0 +1,50 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +static void fix_display() +{ + setenv("DISPLAY", ":0", 1); +} + +static void produce_message(char * type, const char *fmt, va_list args) +{ + char *kdialog_msg; + char buf[1024]; + (void) vsnprintf(buf, sizeof(buf), fmt, args); + asprintf(&kdialog_msg, "%s: %s: %s (error type: %s)", + program_invocation_short_name, type, buf, strerror(errno)); + fprintf(stderr, "%s", kdialog_msg); + switch (fork()) { + case -1: + exit(1); //what else + case 0: + fix_display(); + execlp("kdialog", "kdialog", "--sorry", kdialog_msg, NULL); + exit(1); + default:; + } +} + +void gui_fatal(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + produce_message("Fatal error", fmt, args); + va_end(args); + exit(1); +} + +void gui_nonfatal(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + produce_message("Information", fmt, args); + va_end(args); +} diff --git a/common/gui-fatal.h b/common/gui-fatal.h new file mode 100644 index 00000000..de9799f9 --- /dev/null +++ b/common/gui-fatal.h @@ -0,0 +1,2 @@ +void gui_fatal(const char *fmt, ...); +void gui_nonfatal(const char *fmt, ...); From 66bf0abb5370fc8c327739187664b9b1bd922f36 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 15 Mar 2011 13:12:21 +0100 Subject: [PATCH 30/86] Use gui_fatal in qfile-agent-dvm.c --- appvm/Makefile | 4 ++-- appvm/qfile-agent-dvm.c | 43 +++++++++++++---------------------------- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/appvm/Makefile b/appvm/Makefile index d6bfb6a2..3f81fb94 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -3,8 +3,8 @@ CFLAGS=-Wall -I../common all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm dvm_file_editor: dvm_file_editor.o ../common/ioall.o $(CC) -o dvm_file_editor dvm_file_editor.o ../common/ioall.o -qfile-agent-dvm: qfile-agent-dvm.o ../common/ioall.o - $(CC) -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o +qfile-agent-dvm: qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o + $(CC) -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o diff --git a/appvm/qfile-agent-dvm.c b/appvm/qfile-agent-dvm.c index 194d67cb..5db430cc 100644 --- a/appvm/qfile-agent-dvm.c +++ b/appvm/qfile-agent-dvm.c @@ -9,32 +9,15 @@ #include #include #include +#include #include "dvm2.h" -//the below usage of "system" is smelly, but should be fine - input comes from the same vm -void fatal(char *msg) -{ - char *errcmd; - asprintf(&errcmd, "DISPLAY=:0 kdialog --sorry 'qfile-agent-dvm: %s'", msg); - system(errcmd); - perror(msg); - exit(1); -} - -void nonfatal(char *msg) -{ - char *errcmd; - asprintf(&errcmd, "DISPLAY=:0 kdialog --sorry 'qfile-agent-dvm: %s'", msg); - system(errcmd); - fprintf(stderr, "%s", msg); -} - void send_file(char *fname) { char *base; int fd = open(fname, O_RDONLY); if (fd < 0) - fatal("open file_to_be_edited"); + gui_fatal("open %s", fname); base = rindex(fname, '/'); if (!base) base = fname; @@ -43,9 +26,9 @@ void send_file(char *fname) if (strlen(base) >= DVM_FILENAME_SIZE) base += strlen(base) - DVM_FILENAME_SIZE + 1; if (!write_all(1, base, DVM_FILENAME_SIZE)) - fatal("send filename"); + gui_fatal("send filename to dispVM"); if (!copy_fd_all(1, fd)) - fatal("send file"); + gui_fatal("send file to dispVM"); close(1); } @@ -53,9 +36,9 @@ int copy_and_return_nonemptiness(int tmpfd) { struct stat st; if (!copy_fd_all(tmpfd, 0)) - fatal("receiving file"); + gui_fatal("receiving file from dispVM"); if (fstat(tmpfd, &st)) - fatal("fstat"); + gui_fatal("fstat"); close(tmpfd); return st.st_size; @@ -70,7 +53,7 @@ void recv_file_nowrite(char *fname) asprintf(&tempfile, "/tmp/file_edited_in_dvm.XXXXXX"); tmpfd = mkstemp(tempfile); if (tmpfd < 0) - fatal("unable to create any temporary file, aborting"); + gui_fatal("unable to create any temporary file, aborting"); if (!copy_and_return_nonemptiness(tmpfd)) { unlink(tempfile); return; @@ -79,7 +62,7 @@ void recv_file_nowrite(char *fname) "The file %s has been edited in Disposable VM and the modified content has been received, " "but this file is in nonwritable directory and thus cannot be modified safely. The edited file has been " "saved to %s", fname, tempfile); - nonfatal(errmsg); + gui_nonfatal(errmsg); } void actually_recv_file(char *fname, char *tempfile, int tmpfd) @@ -89,7 +72,7 @@ void actually_recv_file(char *fname, char *tempfile, int tmpfd) return; } if (rename(tempfile, fname)) - fatal("rename"); + gui_fatal("rename"); } void recv_file(char *fname) @@ -121,13 +104,13 @@ void process_spoolentry(char *entry_name) entry_fd = open(abs_spool_entry_name, O_RDONLY); unlink(abs_spool_entry_name); if (entry_fd < 0 || fstat(entry_fd, &st)) - fatal("bad dvm_entry"); + gui_fatal("bad dvm_entry"); entry_size = st.st_size; filename = calloc(1, entry_size + DVM_FILENAME_SIZE); if (!filename) - fatal("malloc"); + gui_fatal("malloc"); if (!read_all(entry_fd, filename, entry_size)) - fatal("read dvm entry"); + gui_fatal("read dvm entry %s", abs_spool_entry_name); close(entry_fd); talk_to_daemon(filename); } @@ -137,7 +120,7 @@ void scan_spool(char *name) struct dirent *ent; DIR *dir = opendir(name); if (!dir) - fatal("opendir"); + gui_fatal("opendir %s", name); while ((ent = readdir(dir))) { char *fname = ent->d_name; if (!strcmp(fname, ".") || !strcmp(fname, "..")) From b8d983cfa905aeaad3cf91c577afb20db27a6612 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 15 Mar 2011 16:07:00 +0100 Subject: [PATCH 31/86] Added qfile-agent --- appvm/Makefile | 6 +- appvm/copy_file.c | 28 ++++++ appvm/filecopy.h | 18 ++++ appvm/qfile-agent.c | 201 +++++++++++++++++++++++++++++++++++++++ rpm_spec/core-appvm.spec | 3 +- 5 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 appvm/copy_file.c create mode 100644 appvm/filecopy.h create mode 100644 appvm/qfile-agent.c diff --git a/appvm/Makefile b/appvm/Makefile index 3f81fb94..c6f4d256 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,10 +1,12 @@ CC=gcc CFLAGS=-Wall -I../common -all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm +all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm qfile-agent dvm_file_editor: dvm_file_editor.o ../common/ioall.o $(CC) -o dvm_file_editor dvm_file_editor.o ../common/ioall.o qfile-agent-dvm: qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o - $(CC) -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o + $(CC) -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o +qfile-agent: qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o + $(CC) -o qfile-agent qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o diff --git a/appvm/copy_file.c b/appvm/copy_file.c new file mode 100644 index 00000000..5f7fc793 --- /dev/null +++ b/appvm/copy_file.c @@ -0,0 +1,28 @@ +#include +#include +extern void notify_progress(int, int); + +char * copy_file(int outfd, int infd, long long size) +{ + char buf[4096]; + long long written = 0; + int ret; + int count; + while (written < size) { + if (size - written > sizeof(buf)) + count = sizeof buf; + else + count = size - written; + ret = read(infd, buf, count); + if (!ret) + return("EOF while reading file"); + if (ret < 0) + return("error reading file"); + if (!write_all(outfd, buf, ret)) + return("error writing file content"); + notify_progress(ret, 0); + written += ret; + } + return NULL; +} + diff --git a/appvm/filecopy.h b/appvm/filecopy.h new file mode 100644 index 00000000..b4f6638c --- /dev/null +++ b/appvm/filecopy.h @@ -0,0 +1,18 @@ +#define FILECOPY_SPOOL "/home/user/.filecopyspool" +#define FILECOPY_VMNAME_SIZE 32 +#define PROGRESS_NOTIFY_DELTA (15*1000*1000) +#define MAX_PATH_LENGTH 16384 + +#define LEGAL_EOF 31415926 + +struct file_header { +unsigned int namelen; +unsigned int mode; +unsigned long long filelen; +unsigned int atime; +unsigned int atime_nsec; +unsigned int mtime; +unsigned int mtime_nsec; +}; + +char * copy_file(int outfd, int infd, long long size); diff --git a/appvm/qfile-agent.c b/appvm/qfile-agent.c new file mode 100644 index 00000000..b4ba354e --- /dev/null +++ b/appvm/qfile-agent.c @@ -0,0 +1,201 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "filecopy.h" + +char *client_flags; +void do_notify_progress(long long total) +{ + FILE *progress; + if (!client_flags[0]) + return; + progress = fopen(client_flags, "w"); + if (!progress) + return; + fprintf(progress, "%d %lld", getpid(), total); + fclose(progress); +} + +void notify_progress(int size, int force) +{ + static long long total = 0; + static long long prev_total = 0; + total += size; + if (total > prev_total + PROGRESS_NOTIFY_DELTA || force) { + do_notify_progress(total); + prev_total = total; + } +} + +void write_headers(struct file_header *hdr, char *filename) +{ + if (!write_all(1, hdr, sizeof(*hdr)) + || !write_all(1, filename, hdr->namelen)) + gui_fatal("writing file headers to remove AppVM"); +} + +int single_file_processor(char *filename, struct stat *st) +{ + struct file_header hdr; + int fd; + mode_t mode = st->st_mode; + + hdr.namelen = strlen(filename) + 1; + hdr.mode = mode; + hdr.atime = st->st_atim.tv_sec; + hdr.atime_nsec = st->st_atim.tv_nsec; + hdr.mtime = st->st_mtim.tv_sec; + hdr.mtime_nsec = st->st_mtim.tv_nsec; + + if (S_ISREG(mode)) { + char * ret; + fd = open(filename, O_RDONLY); + if (!fd) + gui_fatal("open %s", filename); + hdr.filelen = st->st_size; + write_headers(&hdr, filename); + ret=copy_file(1, fd, hdr.filelen); + if (ret) + gui_fatal("Copying file %s: %s", filename, ret); + close(fd); + } + if (S_ISDIR(mode)) { + hdr.filelen = 0; + write_headers(&hdr, filename); + } + if (S_ISLNK(mode)) { + char name[st->st_size + 1]; + if (readlink(filename, name, sizeof(name)) != st->st_size) + gui_fatal("readlink %s", filename); + hdr.filelen = st->st_size + 1; + write_headers(&hdr, filename); + if (!write_all(1, name, st->st_size + 1)) + gui_fatal("write to remote VM"); + } + return 0; +} + +int do_fs_walk(char *file) +{ + char *newfile; + struct stat st; + struct dirent *ent; + DIR *dir; + + if (lstat(file, &st)) + gui_fatal("stat %s", file); + single_file_processor(file, &st); + if (!S_ISDIR(st.st_mode)) + return 0; + dir = opendir(file); + if (!dir) + gui_fatal("opendir %s", file); + while ((ent = readdir(dir))) { + char *fname = ent->d_name; + if (!strcmp(fname, ".") || !strcmp(fname, "..")) + continue; + asprintf(&newfile, "%s/%s", file, fname); + do_fs_walk(newfile); + free(newfile); + } + closedir(dir); + // directory metadata is resent; this makes the code simple, + // and the atime/mtime is set correctly at the second time + single_file_processor(file, &st); + return 0; +} + +void send_vmname(char *vmname) +{ + char buf[FILECOPY_VMNAME_SIZE]; + memset(buf, 0, sizeof(buf)); + strncat(buf, vmname, sizeof(buf) - 1); + if (!write_all(1, buf, sizeof buf)) + gui_fatal("writing vmname to remote VM"); +} + +char *get_item(char *data, char **current, int size) +{ + char *ret; + if ((unsigned long) *current >= (unsigned long) data + size) + return NULL; + ret = *current; + *current += strlen(ret) + 1; + return ret; +} + +void parse_entry(char *data, int datasize) +{ + char *current = data; + char *vmname, *entry, *sep; + vmname = get_item(data, ¤t, datasize); + client_flags = get_item(data, ¤t, datasize); + notify_progress(0, 1); + send_vmname(vmname); + while ((entry = get_item(data, ¤t, datasize))) { + sep = rindex(entry, '/'); + if (!sep) + gui_fatal("Internal error: nonabsolute filenames not allowed"); + *sep = 0; + if (entry[0] == 0) + chdir("/"); + else if (chdir(entry)) + gui_fatal("chdir to %s", entry); + do_fs_walk(sep + 1); + } + notify_progress(0, 1); +} + +void process_spoolentry(char *entry_name) +{ + char *abs_spool_entry_name; + int entry_fd; + struct stat st; + char *entry; + int entry_size; + asprintf(&abs_spool_entry_name, "%s/%s", FILECOPY_SPOOL, + entry_name); + entry_fd = open(abs_spool_entry_name, O_RDONLY); + unlink(abs_spool_entry_name); + if (entry_fd < 0 || fstat(entry_fd, &st)) + gui_fatal("bad file copy spool entry"); + entry_size = st.st_size; + entry = calloc(1, entry_size + 1); + if (!entry) + gui_fatal("malloc"); + if (!read_all(entry_fd, entry, entry_size)) + gui_fatal("read filecopy entry"); + close(entry_fd); + parse_entry(entry, entry_size); +} + +void scan_spool(char *name) +{ + struct dirent *ent; + DIR *dir = opendir(name); + if (!dir) + gui_fatal("opendir %s", name); + while ((ent = readdir(dir))) { + char *fname = ent->d_name; + if (fname[0] != '.') + process_spoolentry(fname); + break; + } + closedir(dir); +} + +int main() +{ + signal(SIGPIPE, SIG_IGN); + scan_spool(FILECOPY_SPOOL); + return 0; +} diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index b947b83b..df0b5907 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -76,7 +76,7 @@ cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm qvm-open-in-dvm2 $RPM_BUILD_RO mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes -cp dvm_file_editor $RPM_BUILD_ROOT/usr/lib/qubes +cp dvm_file_editor qfile-agent $RPM_BUILD_ROOT/usr/lib/qubes ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} @@ -216,6 +216,7 @@ rm -rf $RPM_BUILD_ROOT %attr(4755,root,root) /usr/lib/qubes/qubes_penctl /usr/lib/qubes/qubes_add_pendrive_script /usr/lib/qubes/qrexec_agent +/usr/lib/qubes/qfile-agent /etc/udev/rules.d/qubes.rules /etc/sysconfig/iptables /var/lib/qubes From f0a76204494dea36219ec30c1ca911e954d9b9bf Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 15 Mar 2011 16:19:42 +0100 Subject: [PATCH 32/86] Package qfile-agent-dvm, too. --- rpm_spec/core-appvm.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index df0b5907..3023ffa0 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -76,7 +76,7 @@ cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm qvm-open-in-dvm2 $RPM_BUILD_RO mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes -cp dvm_file_editor qfile-agent $RPM_BUILD_ROOT/usr/lib/qubes +cp dvm_file_editor qfile-agent qfile-agent-dvm $RPM_BUILD_ROOT/usr/lib/qubes ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} @@ -217,6 +217,7 @@ rm -rf $RPM_BUILD_ROOT /usr/lib/qubes/qubes_add_pendrive_script /usr/lib/qubes/qrexec_agent /usr/lib/qubes/qfile-agent +/usr/lib/qubes/qfile-agent-dvm /etc/udev/rules.d/qubes.rules /etc/sysconfig/iptables /var/lib/qubes From 84b1a186ffe485cb6f9f605c54b0b09a53a601cd Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 15 Mar 2011 16:43:43 +0100 Subject: [PATCH 33/86] Added qfile-unpacker and qfile-daemon --- appvm/Makefile | 4 +- appvm/qfile-unpacker.c | 83 +++++++++++++++++++++++++++++++ appvm/unpack.c | 101 ++++++++++++++++++++++++++++++++++++++ dom0/restore/qfile-daemon | 59 ++++++++++++++++++++++ rpm_spec/core-appvm.spec | 3 +- rpm_spec/core-dom0.spec | 2 + 6 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 appvm/qfile-unpacker.c create mode 100644 appvm/unpack.c create mode 100644 dom0/restore/qfile-daemon diff --git a/appvm/Makefile b/appvm/Makefile index c6f4d256..c1abcfad 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,12 +1,14 @@ CC=gcc CFLAGS=-Wall -I../common -all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm qfile-agent +all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm qfile-agent qfile-unpacker dvm_file_editor: dvm_file_editor.o ../common/ioall.o $(CC) -o dvm_file_editor dvm_file_editor.o ../common/ioall.o qfile-agent-dvm: qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o $(CC) -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o qfile-agent: qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o $(CC) -o qfile-agent qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o +qfile-unpacker: qfile-unpacker.o ../common/ioall.o ../common/gui-fatal.o copy_file.o unpack.o + $(CC) -o qfile-unpacker qfile-unpacker.o ../common/ioall.o ../common/gui-fatal.o copy_file.o unpack.o qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o diff --git a/appvm/qfile-unpacker.c b/appvm/qfile-unpacker.c new file mode 100644 index 00000000..eaa5c067 --- /dev/null +++ b/appvm/qfile-unpacker.c @@ -0,0 +1,83 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "filecopy.h" +#define INCOMING_DIR_ROOT "/home/user/incoming" +int prepare_creds_return_uid(char *username) +{ + struct passwd *pwd; + pwd = getpwnam(username); + if (!pwd) { + perror("getpwnam"); + exit(1); + } + setenv("HOME", pwd->pw_dir, 1); + setenv("USER", username, 1); + setgid(pwd->pw_gid); + initgroups(username, pwd->pw_gid); + setfsuid(pwd->pw_uid); + return pwd->pw_uid; +} + +void wait_for_child(int statusfd) +{ + int status; + if (read(statusfd, &status, sizeof status)!=sizeof status) + gui_fatal("File copy error: Internal error reading status from unpacker"); + errno = status; + switch (status) { + case LEGAL_EOF: break; + case 0: gui_fatal("File copy: Connection terminated unexpectedly"); break; + case EINVAL: gui_fatal("File copy: Corrupted data from packer"); break; + case EEXIST: gui_fatal("File copy: not overwriting existing file. Clean ~/incoming, and retry copy"); break; + default: gui_fatal("File copy"); + } +} + +extern void do_unpack(int); + +int main(int argc, char ** argv) +{ + char *incoming_dir; + int pipefds[2]; + int uid; + + pipe(pipefds); + + uid = prepare_creds_return_uid("user"); + + mkdir(INCOMING_DIR_ROOT, 0700); + asprintf(&incoming_dir, "%s/from-%s", INCOMING_DIR_ROOT, argv[1]); + mkdir(incoming_dir, 0700); + if (chdir(incoming_dir)) + gui_fatal("Error chdir to %s", incoming_dir); + switch (fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + if (chroot(incoming_dir)) //impossible + gui_fatal("Error chroot to %s", incoming_dir); + setuid(uid); + close(pipefds[0]); + do_unpack(pipefds[1]); + exit(0); + default:; + } + + setuid(uid); + close(pipefds[1]); + wait_for_child(pipefds[0]); + + return 0; +} diff --git a/appvm/unpack.c b/appvm/unpack.c new file mode 100644 index 00000000..c0353c11 --- /dev/null +++ b/appvm/unpack.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "filecopy.h" + +char namebuf[MAX_PATH_LENGTH]; +void notify_progress(int p1, int p2) +{ +} + +int global_status_fd; +void do_exit(int code) +{ + int codebuf = code; + write(global_status_fd, &codebuf, sizeof codebuf); + exit(0); +} + + +void fix_times_and_perms(struct file_header *hdr, char *name) +{ + struct timeval times[2] = + { {hdr->atime, hdr->atime_nsec / 1000}, {hdr->mtime, + hdr->mtime_nsec / 1000} + }; + if (chmod(name, hdr->mode & 07777)) + do_exit(errno); + if (utimes(name, times)) + do_exit(errno); +} + + + +void process_one_file_reg(struct file_header *hdr, char *name) +{ + char *ret; + int fdout = + open(name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0700); + if (fdout < 0) + do_exit(errno); + ret = copy_file(fdout, 0, hdr->filelen); + if (ret) + do_exit(errno); + close(fdout); + fix_times_and_perms(hdr, name); +} + + +void process_one_file_dir(struct file_header *hdr, char *name) +{ + if (mkdir(name, 0700) && errno != EEXIST) + do_exit(errno); + fix_times_and_perms(hdr, name); +} + +void process_one_file_link(struct file_header *hdr, char *name) +{ + char content[MAX_PATH_LENGTH]; + if (hdr->filelen > MAX_PATH_LENGTH - 1) + do_exit(ENAMETOOLONG); + if (!read_all(0, content, hdr->filelen)) + do_exit(errno); + content[hdr->filelen] = 0; + if (symlink(content, name)) + do_exit(errno); + +} + +void process_one_file(struct file_header *hdr) +{ + if (hdr->namelen > MAX_PATH_LENGTH - 1) + do_exit(ENAMETOOLONG); + if (!read_all(0, namebuf, hdr->namelen)) + do_exit(errno); + namebuf[hdr->namelen] = 0; + if (S_ISREG(hdr->mode)) + process_one_file_reg(hdr, namebuf); + else if (S_ISLNK(hdr->mode)) + process_one_file_link(hdr, namebuf); + else if (S_ISDIR(hdr->mode)) + process_one_file_dir(hdr, namebuf); + else + do_exit(EINVAL); +} + +void do_unpack(int fd) +{ + global_status_fd = fd; + struct file_header hdr; + while (read_all(0, &hdr, sizeof hdr)) + process_one_file(&hdr); + if (errno) + do_exit(errno); + else + do_exit(LEGAL_EOF); +} diff --git a/dom0/restore/qfile-daemon b/dom0/restore/qfile-daemon new file mode 100644 index 00000000..6b589279 --- /dev/null +++ b/dom0/restore/qfile-daemon @@ -0,0 +1,59 @@ +#!/usr/bin/python2.6 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Rafal Wojtczuk +# +# 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. +# +# +import os +import sys +import subprocess +from qubes.qubes import QubesVmCollection + +def is_copy_allowed(vm): +# if vm.copy_allowed: +# return True + q = 'Do you authorize file copy from ' + q+= os.getenv("QREXEC_REMOTE_DOMAIN") + q+= ' to ' + vm.name + ' ?' + retcode = subprocess.call(['/usr/bin/kdialog', '--yesno', q, '--title', 'File transfer confirmation']) + return retcode == 0 + +def main(): + FILECOPY_VMNAME_SIZE = 32 + blob=os.read(0, FILECOPY_VMNAME_SIZE) + vmname = blob.split("\x00")[0] + + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + vm = qvm_collection.get_vm_by_name(vmname) +# we do not want to flood dom0 with error windows; so just log to stderr + if vm is None: + print >> sys.stderr, 'Domain ' + vmname + ' does not exist ?' + exit(1) + if not vm.is_running(): + print >> sys.stderr, 'Domain ' + vmname + ' is not running ?' + exit(1) + if not is_copy_allowed(vm): + exit(1) + cmd = "root:/usr/lib/qubes/qfile-unpacker " + os.getenv("QREXEC_REMOTE_DOMAIN") + os.execl("/usr/lib/qubes/qrexec_client", "qrexec_client", "-d", vmname, cmd) + +main() diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index 3023ffa0..f3c875d4 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -76,7 +76,7 @@ cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm qvm-open-in-dvm2 $RPM_BUILD_RO mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes -cp dvm_file_editor qfile-agent qfile-agent-dvm $RPM_BUILD_ROOT/usr/lib/qubes +cp dvm_file_editor qfile-agent qfile-agent-dvm qfile-unpacker $RPM_BUILD_ROOT/usr/lib/qubes ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} @@ -218,6 +218,7 @@ rm -rf $RPM_BUILD_ROOT /usr/lib/qubes/qrexec_agent /usr/lib/qubes/qfile-agent /usr/lib/qubes/qfile-agent-dvm +/usr/lib/qubes/qfile-unpacker /etc/udev/rules.d/qubes.rules /etc/sysconfig/iptables /var/lib/qubes diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 89cbd284..ed72a347 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -96,6 +96,7 @@ cp restore/xenstore-watch restore/qvm-create-default-dvm $RPM_BUILD_ROOT/usr/bin cp restore/qubes_restore restore/xenfreepages $RPM_BUILD_ROOT/usr/lib/qubes cp restore/qubes_prepare_saved_domain.sh $RPM_BUILD_ROOT/usr/lib/qubes cp restore/qfile-daemon-dvm $RPM_BUILD_ROOT/usr/lib/qubes +cp restore/qfile-daemon $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/vm-templates @@ -265,6 +266,7 @@ fi /usr/lib/qubes/qmemman_daemon.py* /usr/lib/qubes/meminfo-writer /usr/lib/qubes/qfile-daemon-dvm* +/usr/lib/qubes/qfile-daemon %attr(770,root,qubes) %dir /var/lib/qubes %attr(770,root,qubes) %dir /var/lib/qubes/vm-templates %attr(770,root,qubes) %dir /var/lib/qubes/appvms From 8ce0e0f39b210f5f0a3c82d30d33671f3e4a35e9 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 15 Mar 2011 16:48:17 +0100 Subject: [PATCH 34/86] Fixed permissions of qfile-daemon --- dom0/restore/qfile-daemon | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 dom0/restore/qfile-daemon diff --git a/dom0/restore/qfile-daemon b/dom0/restore/qfile-daemon old mode 100644 new mode 100755 From 2ea7a0e77a27b3fd727db9eafff0f3852e42ecd6 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 10:11:55 +0100 Subject: [PATCH 35/86] Build filecopy tools with -g. --- appvm/Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/appvm/Makefile b/appvm/Makefile index c1abcfad..56f90913 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,14 +1,14 @@ CC=gcc -CFLAGS=-Wall -I../common +CFLAGS=-g -Wall -I../common all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm qfile-agent qfile-unpacker dvm_file_editor: dvm_file_editor.o ../common/ioall.o - $(CC) -o dvm_file_editor dvm_file_editor.o ../common/ioall.o + $(CC) -g -o dvm_file_editor dvm_file_editor.o ../common/ioall.o qfile-agent-dvm: qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o - $(CC) -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o + $(CC) -g -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o qfile-agent: qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o - $(CC) -o qfile-agent qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o + $(CC) -g -o qfile-agent qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o qfile-unpacker: qfile-unpacker.o ../common/ioall.o ../common/gui-fatal.o copy_file.o unpack.o - $(CC) -o qfile-unpacker qfile-unpacker.o ../common/ioall.o ../common/gui-fatal.o copy_file.o unpack.o + $(CC) -g -o qfile-unpacker qfile-unpacker.o ../common/ioall.o ../common/gui-fatal.o copy_file.o unpack.o qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o From e6da61cb5e12553dbc3fa99b329c830f9c179d64 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 10:14:39 +0100 Subject: [PATCH 36/86] Scan filecopy sppool properly. --- appvm/qfile-agent.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/appvm/qfile-agent.c b/appvm/qfile-agent.c index b4ba354e..7ab2639a 100644 --- a/appvm/qfile-agent.c +++ b/appvm/qfile-agent.c @@ -186,9 +186,10 @@ void scan_spool(char *name) gui_fatal("opendir %s", name); while ((ent = readdir(dir))) { char *fname = ent->d_name; - if (fname[0] != '.') + if (fname[0] != '.') { process_spoolentry(fname); - break; + break; + } } closedir(dir); } From 5230c1293405e74817623b5efd0cb400fc2c0118 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 10:48:27 +0100 Subject: [PATCH 37/86] qfile-agent: Handle filenames with trailing slash properly. --- appvm/qfile-agent.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/appvm/qfile-agent.c b/appvm/qfile-agent.c index 7ab2639a..71c86e5f 100644 --- a/appvm/qfile-agent.c +++ b/appvm/qfile-agent.c @@ -57,15 +57,15 @@ int single_file_processor(char *filename, struct stat *st) hdr.mtime_nsec = st->st_mtim.tv_nsec; if (S_ISREG(mode)) { - char * ret; + char *ret; fd = open(filename, O_RDONLY); if (!fd) gui_fatal("open %s", filename); hdr.filelen = st->st_size; write_headers(&hdr, filename); - ret=copy_file(1, fd, hdr.filelen); + ret = copy_file(1, fd, hdr.filelen); if (ret) - gui_fatal("Copying file %s: %s", filename, ret); + gui_fatal("Copying file %s: %s", filename, ret); close(fd); } if (S_ISDIR(mode)) { @@ -142,10 +142,13 @@ void parse_entry(char *data, int datasize) notify_progress(0, 1); send_vmname(vmname); while ((entry = get_item(data, ¤t, datasize))) { - sep = rindex(entry, '/'); - if (!sep) - gui_fatal("Internal error: nonabsolute filenames not allowed"); - *sep = 0; + do { + sep = rindex(entry, '/'); + if (!sep) + gui_fatal + ("Internal error: nonabsolute filenames not allowed"); + *sep = 0; + } while (sep[1] == 0); if (entry[0] == 0) chdir("/"); else if (chdir(entry)) @@ -188,8 +191,8 @@ void scan_spool(char *name) char *fname = ent->d_name; if (fname[0] != '.') { process_spoolentry(fname); - break; - } + break; + } } closedir(dir); } From b01464670b5fa7d6e6006f7f866dfb61c4957cee Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 10:50:11 +0100 Subject: [PATCH 38/86] New qvm-copy-to-vm, aka qvm-copy-to-vm2 --- appvm/qvm-copy-to-vm2 | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100755 appvm/qvm-copy-to-vm2 diff --git a/appvm/qvm-copy-to-vm2 b/appvm/qvm-copy-to-vm2 new file mode 100755 index 00000000..ddababe7 --- /dev/null +++ b/appvm/qvm-copy-to-vm2 @@ -0,0 +1,47 @@ +#!/bin/sh +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Rafal Wojtczuk +# +# 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. +# +# +set -x + +if [ $# -lt 2 ] ; then + echo usage: $0 'vmname file [file]*' + exit 1 +fi + +FILECOPY_SPOOL=/home/user/.filecopyspool +if ! [ -e $FILECOPY_SPOOL ] ; then + mkdir $FILECOPY_SPOOL +fi + +REQ_FILE_TMP=$FILECOPY_SPOOL/.req.$$ +echo -ne "$1""\x00" > $REQ_FILE_TMP +echo -ne "$PROGRESS_FILE""\x00" >> $REQ_FILE_TMP + +shift +for FILE in "$@" ; do + if ! [ "X""${FILE:0:1}" = X/ ] ; then + FILE="$PWD"/"$FILE" + fi + echo -ne "$FILE""\x00" >> $REQ_FILE_TMP +done + +mv $REQ_FILE_TMP $FILECOPY_SPOOL/req.$$ +echo -n FCPR > /var/run/qubes/qrexec_agent From ecf007b3a2c45d0eb5060ca1e204d20af0c89fc0 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 11:06:27 +0100 Subject: [PATCH 39/86] qfile-agent writes DONE to the status file at the end of work. --- appvm/qfile-agent.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/appvm/qfile-agent.c b/appvm/qfile-agent.c index 71c86e5f..f7e27a98 100644 --- a/appvm/qfile-agent.c +++ b/appvm/qfile-agent.c @@ -12,8 +12,16 @@ #include #include "filecopy.h" +enum { + PROGRESS_FLAG_NORMAL, + PROGRESS_FLAG_INIT, + PROGRESS_FLAG_DONE +}; + + + char *client_flags; -void do_notify_progress(long long total) +void do_notify_progress(long long total, int flag) { FILE *progress; if (!client_flags[0]) @@ -21,17 +29,19 @@ void do_notify_progress(long long total) progress = fopen(client_flags, "w"); if (!progress) return; - fprintf(progress, "%d %lld", getpid(), total); + fprintf(progress, "%d %lld %s", getpid(), total, + flag == PROGRESS_FLAG_DONE ? "DONE" : "BUSY"); fclose(progress); } -void notify_progress(int size, int force) +void notify_progress(int size, int flag) { static long long total = 0; static long long prev_total = 0; total += size; - if (total > prev_total + PROGRESS_NOTIFY_DELTA || force) { - do_notify_progress(total); + if (total > prev_total + PROGRESS_NOTIFY_DELTA + || (flag != PROGRESS_FLAG_NORMAL)) { + do_notify_progress(total, flag); prev_total = total; } } @@ -139,7 +149,7 @@ void parse_entry(char *data, int datasize) char *vmname, *entry, *sep; vmname = get_item(data, ¤t, datasize); client_flags = get_item(data, ¤t, datasize); - notify_progress(0, 1); + notify_progress(0, PROGRESS_FLAG_INIT); send_vmname(vmname); while ((entry = get_item(data, ¤t, datasize))) { do { @@ -155,7 +165,7 @@ void parse_entry(char *data, int datasize) gui_fatal("chdir to %s", entry); do_fs_walk(sep + 1); } - notify_progress(0, 1); + notify_progress(0, PROGRESS_FLAG_DONE); } void process_spoolentry(char *entry_name) From 2938ee53565523d64b6501b4a0d943d8ede6deea Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 11:07:27 +0100 Subject: [PATCH 40/86] removed set -x from qvm-copy-to-vm2 --- appvm/qvm-copy-to-vm2 | 1 - 1 file changed, 1 deletion(-) diff --git a/appvm/qvm-copy-to-vm2 b/appvm/qvm-copy-to-vm2 index ddababe7..56dcdef8 100755 --- a/appvm/qvm-copy-to-vm2 +++ b/appvm/qvm-copy-to-vm2 @@ -19,7 +19,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # -set -x if [ $# -lt 2 ] ; then echo usage: $0 'vmname file [file]*' From 7dbe6e1731edf369a1f5ec203101ee29da3aa6fe Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Tue, 15 Mar 2011 20:27:37 +0100 Subject: [PATCH 41/86] Create NetVM xen config from separate template (netvm-template.conf) --- dom0/qvm-core/qubes.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index f8e344a5..d2f02e2e 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -63,6 +63,7 @@ default_rootcow_img = "root-cow.img" default_swapcow_img = "swap-cow.img" default_private_img = "private.img" default_appvms_conf_file = "appvm-template.conf" +default_netvms_conf_file = "netvm-template.conf" default_templatevm_conf_template = "templatevm.conf" # needed for TemplateVM cloning default_appmenus_templates_subdir = "apps.templates" default_kernels_subdir = "kernels" @@ -637,6 +638,12 @@ class QubesTemplateVm(QubesVm): self.appvms_conf_file = dir_path + "/" + ( appvms_conf_file if appvms_conf_file is not None else default_appvms_conf_file) + if netvms_conf_file is not None and os.path.isabs(netvms_conf_file): + self.netvms_conf_file = netvms_conf_file + else: + self.netvms_conf_file = dir_path + "/" + ( + netvms_conf_file if netvms_conf_file is not None else default_netvms_conf_file) + self.templatevm_conf_template = self.dir_path + "/" + default_templatevm_conf_template self.kernels_dir = self.dir_path + "/" + default_kernels_subdir self.appmenus_templates_dir = self.dir_path + "/" + default_appmenus_templates_subdir @@ -695,6 +702,11 @@ class QubesTemplateVm(QubesVm): format(src_template_vm.appvms_conf_file, self.appvms_conf_file) shutil.copy (src_template_vm.appvms_conf_file, self.appvms_conf_file) + if verbose: + print "--> Copying the VM config template :\n{0} ==>\n{1}".\ + format(src_template_vm.netvms_conf_file, self.netvms_conf_file) + shutil.copy (src_template_vm.netvms_conf_file, self.netvms_conf_file) + if verbose: print "--> Copying the template's private image:\n{0} ==>\n{1}".\ format(src_template_vm.private_img, self.private_img) @@ -816,6 +828,7 @@ class QubesTemplateVm(QubesVm): dir_path=self.dir_path, conf_file=self.conf_file, appvms_conf_file=self.appvms_conf_file, + netvms_conf_file=self.netvms_conf_file, root_img=self.root_img, rootcow_img=self.rootcow_img, private_img=self.private_img, @@ -880,7 +893,11 @@ class QubesCowVm(QubesVm): raise QubesException ("TemaplteVM is updateable: cannot make the template based VM '{0}' updateable".format(self.name)) def create_config_file(self): - conf_template = open (self.template_vm.appvms_conf_file, "r") + conf_template = None + if self.type == "NetVM": + conf_template = open (self.template_vm.netvms_conf_file, "r") + else: + conf_template = open (self.template_vm.appvms_conf_file, "r") if os.path.isfile(self.conf_file): shutil.copy(self.conf_file, self.conf_file + ".backup") conf_appvm = open(self.conf_file, "w") @@ -1814,7 +1831,8 @@ class QubesVmCollection(dict): kwargs = {} attr_list = ("qid", "name", "dir_path", "conf_file", - "appvms_conf_file", "private_img", "root_img", + "appvms_conf_file", "appvms_conf_file", + "private_img", "root_img", "installed_by_rpm", "updateable", "uses_default_netvm") From 33ed1ecad867c18a0662f185bd2ec443086bf231 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Tue, 15 Mar 2011 23:05:48 +0100 Subject: [PATCH 42/86] Drop forced fedora version from requires --- rpm_spec/core-appvm.spec | 2 +- rpm_spec/core-commonvm.spec | 2 +- rpm_spec/core-netvm.spec | 2 +- rpm_spec/core-proxyvm.spec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index 91fd13c6..56c62ba3 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -32,7 +32,7 @@ Vendor: Invisible Things Lab License: GPL URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read -Requires: fedora-release = 13 +Requires: fedora-release Requires: /usr/bin/mimeopen Requires: qubes-core-commonvm BuildRequires: gcc diff --git a/rpm_spec/core-commonvm.spec b/rpm_spec/core-commonvm.spec index 6705c28b..da3decdc 100644 --- a/rpm_spec/core-commonvm.spec +++ b/rpm_spec/core-commonvm.spec @@ -32,7 +32,7 @@ Vendor: Invisible Things Lab License: GPL URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read -Requires: fedora-release = 13 +Requires: fedora-release %define _builddir %(pwd)/common diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index 346c548e..97af562d 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -32,7 +32,7 @@ Vendor: Invisible Things Lab License: GPL URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read -Requires: fedora-release = 13 +Requires: fedora-release Requires: NetworkManager >= 0.8.1-1 Requires: qubes-core-commonvm Provides: qubes-core-vm diff --git a/rpm_spec/core-proxyvm.spec b/rpm_spec/core-proxyvm.spec index 52bc17f8..a121a190 100644 --- a/rpm_spec/core-proxyvm.spec +++ b/rpm_spec/core-proxyvm.spec @@ -33,7 +33,7 @@ License: GPL URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read Requires: /sbin/ethtool -Requires: fedora-release = 13 +Requires: fedora-release Requires: qubes-core-netvm %define _builddir %(pwd)/proxyvm From 01a1aeb4033d7278807bd4a2cd7780ac412eefd6 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 16 Mar 2011 08:41:41 +0100 Subject: [PATCH 43/86] Do not try to disable 'reboot' service --- rpm_spec/core-commonvm.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/rpm_spec/core-commonvm.spec b/rpm_spec/core-commonvm.spec index da3decdc..53eb1953 100644 --- a/rpm_spec/core-commonvm.spec +++ b/rpm_spec/core-commonvm.spec @@ -96,6 +96,7 @@ do [ $srv = 'killall' ] && continue [ $srv = 'halt' ] && continue [ $srv = 'single' ] && continue + [ $srv = 'reboot' ] && continue [ $srv = 'qubes_gui' ] && continue chkconfig $srv off done From 5acc4610b495c9ed50be865f5d041daded438e3a Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 16 Mar 2011 11:28:16 +0100 Subject: [PATCH 44/86] Allow installed_by_rpm=False in NetVM and ProxyVM --- dom0/qvm-core/qubes.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index d2f02e2e..a93a1e55 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1035,7 +1035,7 @@ class QubesNetVm(QubesCowVm): if "label" not in kwargs or kwargs["label"] is None: kwargs["label"] = default_servicevm_label - super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs) + super(QubesNetVm, self).__init__(**kwargs) @property def type(self): @@ -1612,14 +1612,14 @@ class QubesVmCollection(dict): def add_new_netvm(self, name, template_vm, dir_path = None, conf_file = None, - private_img = None, + private_img = None, installed_by_rpm = False, label = None): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() vm = QubesNetVm (qid=qid, name=name, template_vm=template_vm, netid=netid, label=label, - private_img=private_img, + private_img=private_img, installed_by_rpm=installed_by_rpm, dir_path=dir_path, conf_file=conf_file) if not self.verify_new_vm (vm): @@ -1633,14 +1633,14 @@ class QubesVmCollection(dict): def add_new_proxyvm(self, name, template_vm, dir_path = None, conf_file = None, - private_img = None, + private_img = None, installed_by_rpm = False, label = None): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() vm = QubesProxyVm (qid=qid, name=name, template_vm=template_vm, netid=netid, label=label, - private_img=private_img, + private_img=private_img, installed_by_rpm=installed_by_rpm, dir_path=dir_path, conf_file=conf_file, netvm_vm = self.get_default_fw_netvm_vm()) From 72ddb5aae198baac4912dfc57a353d48f86ac5d5 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Tue, 15 Mar 2011 18:51:31 +0100 Subject: [PATCH 45/86] Do not add new vm to xen storage in qvm-create - it is done by core --- dom0/qvm-tools/qvm-create | 1 - 1 file changed, 1 deletion(-) diff --git a/dom0/qvm-tools/qvm-create b/dom0/qvm-tools/qvm-create index a16f19ba..3a05cdbf 100755 --- a/dom0/qvm-tools/qvm-create +++ b/dom0/qvm-tools/qvm-create @@ -129,7 +129,6 @@ def main(): vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label) try: vm.create_on_disk(verbose=options.verbose) - vm.add_to_xen_storage() except (IOError, OSError) as err: print "ERROR: {0}".format(err) From 1c505589c1cfe8246a29b9fb36bc47b77a4f8355 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Tue, 15 Mar 2011 19:47:26 +0100 Subject: [PATCH 46/86] Move xenstore-watch for VM from AppVM to common. Add to core-common.spec --- appvm/.gitignore | 1 - appvm/Makefile | 4 +--- common/.gitignore | 1 + common/Makefile | 4 +++- {appvm => common}/xenstore-watch.c | 0 rpm_spec/core-commonvm.spec | 12 +++++++++--- 6 files changed, 14 insertions(+), 8 deletions(-) rename {appvm => common}/xenstore-watch.c (100%) diff --git a/appvm/.gitignore b/appvm/.gitignore index 23680333..edd6d099 100644 --- a/appvm/.gitignore +++ b/appvm/.gitignore @@ -1,4 +1,3 @@ qubes_add_pendrive_script qubes_penctl qvm-open-in-dvm -xenstore-watch diff --git a/appvm/Makefile b/appvm/Makefile index 0ef375a7..858a665b 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,14 +1,12 @@ CC=gcc CFLAGS=-Wall -all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm xenstore-watch +all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o $(CC) -o qubes_add_pendrive_script qubes_add_pendrive_script.o -lxenstore qvm-open-in-dvm: qvm-open-in-dvm.o $(CC) -o qvm-open-in-dvm qvm-open-in-dvm.o -lxenstore -xenstore-watch: xenstore-watch.o - $(CC) -o xenstore-watch xenstore-watch.o -lxenstore clean: rm -f qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm xenstore-watch *.o *~ diff --git a/common/.gitignore b/common/.gitignore index 03034a95..b87d6e19 100644 --- a/common/.gitignore +++ b/common/.gitignore @@ -1 +1,2 @@ meminfo-writer +xenstore-watch diff --git a/common/Makefile b/common/Makefile index 85888a90..4f8df460 100644 --- a/common/Makefile +++ b/common/Makefile @@ -1,7 +1,9 @@ CC=gcc CFLAGS=-Wall -g -O3 -all: meminfo-writer +all: meminfo-writer xenstore-watch meminfo-writer: meminfo-writer.o $(CC) -g -o meminfo-writer meminfo-writer.o -lxenstore +xenstore-watch: xenstore-watch.o + $(CC) -o xenstore-watch xenstore-watch.o -lxenstore clean: rm -f meminfo-writer *.o *~ diff --git a/appvm/xenstore-watch.c b/common/xenstore-watch.c similarity index 100% rename from appvm/xenstore-watch.c rename to common/xenstore-watch.c diff --git a/rpm_spec/core-commonvm.spec b/rpm_spec/core-commonvm.spec index 53eb1953..3a1c4051 100644 --- a/rpm_spec/core-commonvm.spec +++ b/rpm_spec/core-commonvm.spec @@ -39,6 +39,9 @@ Requires: fedora-release %description The Qubes core files for installation inside a Qubes VM. +%build +make + %pre if [ "$1" != 1 ] ; then @@ -62,10 +65,12 @@ mkdir -p $RPM_BUILD_ROOT/etc/sysconfig cp iptables $RPM_BUILD_ROOT/etc/sysconfig/ mkdir -p $RPM_BUILD_ROOT/etc/yum.repos.d cp ../appvm/qubes.repo $RPM_BUILD_ROOT/etc/yum.repos.d -mkdir -p $RPM_BUILD_ROOT/sbin -cp ../common/qubes_serial_login $RPM_BUILD_ROOT/sbin +mkdir -p $RPM_BUILD_ROOT/sbin +cp qubes_serial_login $RPM_BUILD_ROOT/sbin +mkdir -p $RPM_BUILD_ROOT/usr/bin +cp xenstore-watch $RPM_BUILD_ROOT/usr/bin mkdir -p $RPM_BUILD_ROOT/etc -cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ +cp serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ %triggerin -- initscripts cp /var/lib/qubes/serial.conf /etc/init/serial.conf @@ -165,3 +170,4 @@ rm -rf $RPM_BUILD_ROOT /var/lib/qubes /etc/yum.repos.d/qubes.repo /sbin/qubes_serial_login +/usr/bin/xenstore-watch From 5e2dd1c6cef730ef123b7152bcf24e1db7241b49 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 16 Mar 2011 11:44:25 +0100 Subject: [PATCH 47/86] Revert "Do not add new vm to xen storage in qvm-create - it is done by core" This reverts commit 72ddb5aae198baac4912dfc57a353d48f86ac5d5. --- dom0/qvm-tools/qvm-create | 1 + 1 file changed, 1 insertion(+) diff --git a/dom0/qvm-tools/qvm-create b/dom0/qvm-tools/qvm-create index 3a05cdbf..a16f19ba 100755 --- a/dom0/qvm-tools/qvm-create +++ b/dom0/qvm-tools/qvm-create @@ -129,6 +129,7 @@ def main(): vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label) try: vm.create_on_disk(verbose=options.verbose) + vm.add_to_xen_storage() except (IOError, OSError) as err: print "ERROR: {0}".format(err) From 821f707053ed0d85daffd5e7de42dc57e4772b9d Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 12:00:22 +0100 Subject: [PATCH 48/86] Added qvm-copy-to-vm2.kde --- appvm/qvm-copy-to-vm2.kde | 48 +++++++++++++++++++++++++++++++++++++++ appvm/qvm-copy.desktop | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100755 appvm/qvm-copy-to-vm2.kde diff --git a/appvm/qvm-copy-to-vm2.kde b/appvm/qvm-copy-to-vm2.kde new file mode 100755 index 00000000..879279be --- /dev/null +++ b/appvm/qvm-copy-to-vm2.kde @@ -0,0 +1,48 @@ +#!/bin/sh +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Rafal Wojtczuk +# +# 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. +# +# + +VM=$(kdialog -inputbox "Enter the VM name to send files to:") +if [ X$VM = X ] ; then exit 0 ; fi + +SIZE=$(du -c "$@" | tail -1 | cut -f 1) +REF=$(kdialog --progressbar "Copy progress") +qdbus $REF org.freedesktop.DBus.Properties.Set "" maximum $SIZE + +export PROGRESS_FILE=$(mktemp) +qvm-copy-to-vm2 $VM "$@" +while ! [ -s $PROGRESS_FILE ] ; do + sleep 0.1 +done +while true ; do + read agentpid sentsize agentstatus < $PROGRESS_FILE + if ! [ -e /proc/$agentpid ] ; then break ; fi + if [ "x"$agentstatus = xdone ] ; then break ; fi + CURRSIZE=$(($sentsize/1024)) + qdbus $REF org.freedesktop.DBus.Properties.Set "" value $CURRSIZE + sleep 0.4 +done + +qdbus $REF close +rm -f $PROGRESS_FILE +if ! [ "x"$agentstatus = xDONE ] ; then + kdialog --sorry 'Abnormal file copy termination; see /var/log/qubes/qrexec.xid.log in dom0 for more details' +fi diff --git a/appvm/qvm-copy.desktop b/appvm/qvm-copy.desktop index 5795eb61..4d5e800f 100644 --- a/appvm/qvm-copy.desktop +++ b/appvm/qvm-copy.desktop @@ -4,7 +4,7 @@ Type=Service X-KDE-ServiceTypes=KonqPopupMenu/Plugin,inode/directory,all/allfiles [Desktop Action QvmCopy] -Exec=/usr/lib/qubes/qvm-copy-to-vm.kde %U +Exec=/usr/lib/qubes/qvm-copy-to-vm2.kde %U Icon=kget Name=Send To VM From a195f436b72f95926f693584ccdddf7ecf726213 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 12:48:29 +0100 Subject: [PATCH 49/86] In qfile-unpacker, set perms on the directory only on second pass. It solves problem with transferring r.x directory. Originally, it would fail when creating files in the directory (as it is not writable). Now, we will create it rwx, create files in it, and fix perms and utimes on the second pass. [user@devel fcopy]$ ls -ald /boot dr-xr-xr-x 4 root root 4096 Sep 1 2010 /boot --- appvm/unpack.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/appvm/unpack.c b/appvm/unpack.c index c0353c11..ad53ebf5 100644 --- a/appvm/unpack.c +++ b/appvm/unpack.c @@ -53,7 +53,11 @@ void process_one_file_reg(struct file_header *hdr, char *name) void process_one_file_dir(struct file_header *hdr, char *name) { - if (mkdir(name, 0700) && errno != EEXIST) +// fix perms only when the directory is sent for the second time +// it allows to transfer r.x directory contents, as we create it rwx initially + if (!mkdir(name, 0700)) + return; + if (errno != EEXIST) do_exit(errno); fix_times_and_perms(hdr, name); } From 777eaa2168bc2a4e7d0a404010d8ec2ec5433c73 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 12:58:40 +0100 Subject: [PATCH 50/86] In read_all()/write_all(), continue upon EINTR. --- common/ioall.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/ioall.c b/common/ioall.c index 413477fc..a99a8aac 100644 --- a/common/ioall.c +++ b/common/ioall.c @@ -31,6 +31,8 @@ int write_all(int fd, void *buf, int size) int ret; while (written < size) { ret = write(fd, (char *) buf + written, size - written); + if (ret == -1 && errno == EINTR) + continue; if (ret <= 0) { perror("write"); return 0; @@ -47,6 +49,8 @@ int read_all(int fd, void *buf, int size) int ret; while (got_read < size) { ret = read(fd, (char *) buf + got_read, size - got_read); + if (ret == -1 && errno == EINTR) + continue; if (ret == 0) { errno = 0; fprintf(stderr, "EOF\n"); @@ -68,6 +72,8 @@ int copy_fd_all(int fdout, int fdin) char buf[4096]; for (;;) { ret = read(fdin, buf, sizeof(buf)); + if (ret == -1 && errno == EINTR) + continue; if (!ret) break; if (ret < 0) { @@ -81,4 +87,3 @@ int copy_fd_all(int fdout, int fdin) } return 1; } - From 27cfd6111a49eda01057453ca060020f69bc7dfc Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 14:21:45 +0100 Subject: [PATCH 51/86] qrexec_daemon limits the number of its children So that evil VM cannot just send flood of exec qfile-daemon requests, and DoS dom0. --- qrexec/qrexec_daemon.c | 47 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index c955dbd2..8507c90f 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "qrexec.h" #include "buffer.h" @@ -55,6 +56,8 @@ void handle_usr1(int x) exit(0); } +void sigchld_handler(int x); + char *remote_domain_name; void init(int xid) @@ -97,7 +100,7 @@ void init(int xid) setuid(getuid()); server_fd = get_server_socket(xid, remote_domain_name); signal(SIGPIPE, SIG_IGN); - signal(SIGCHLD, SIG_IGN); + signal(SIGCHLD, sigchld_handler); signal(SIGUSR1, SIG_DFL); kill(getppid(), SIGUSR1); } @@ -252,10 +255,48 @@ void pass_to_client(int clid, struct client_header *hdr) } } +int children_count; +int child_exited; + +void sigchld_handler(int x) +{ + child_exited = 1; + signal(SIGCHLD, sigchld_handler); +} + +void reap_children() +{ + int status; + while (waitpid(-1, &status, WNOHANG) > 0) + children_count--; + child_exited = 0; +} + +void wait_for_child() +{ + int status; + waitpid(-1, &status, 0); + children_count--; +} + +#define MAX_CHILDREN 10 +void check_children_count() +{ + if (children_count > MAX_CHILDREN) { + fprintf(stderr, + "max number of children reached, waiting for child exit...\n"); + wait_for_child(); + fprintf(stderr, "now children_count=%d, continuing.\n", + children_count); + } +} + void handle_trigger_exec(int req) { char *rcmd = NULL, *lcmd = NULL; int i; + + check_children_count(); switch (req) { case QREXEC_EXECUTE_FILE_COPY: rcmd = "directly:user:/usr/lib/qubes/qfile-agent"; @@ -276,6 +317,7 @@ void handle_trigger_exec(int req) case 0: break; default: + children_count++; return; } for (i = 3; i < 256; i++) @@ -405,5 +447,8 @@ int main(int argc, char **argv) if (clients[i].state != CLIENT_INVALID && FD_ISSET(i, &wrset)) flush_client_data_daemon(i); + if (child_exited) + reap_children(); + } } From 769eedd33a8c988ba37bef3b53d53c4593c814b6 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 14:52:35 +0100 Subject: [PATCH 52/86] Make qrexec_client wait for its local child before exiting If we do not wait and exit imemdiately, qrexec_daemon will decrease the children count and continue spawning processes, while e.g. qfile-daemon still waits for kdialog - so dom0 will be DoSed by multiple processes. --- qrexec/qrexec_client.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/qrexec/qrexec_client.c b/qrexec/qrexec_client.c index 23a4dac1..c378a550 100644 --- a/qrexec/qrexec_client.c +++ b/qrexec/qrexec_client.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "qrexec.h" #include "buffer.h" #include "glue.h" @@ -58,6 +59,18 @@ void do_exec(char *prog) int local_stdin_fd, local_stdout_fd; +void do_exit(int code) +{ + int status; +// sever communication lines; wait for child, if any +// so that qrexec-daemon can count (recursively) spawned processes correctly + close(local_stdin_fd); + close(local_stdout_fd); + waitpid(-1, &status, 0); + exit(code); +} + + void prepare_local_fds(char *cmdline) { int pid; @@ -79,7 +92,7 @@ void send_cmdline(int s, int type, char *cmdline) if (!write_all(s, &hdr, sizeof(hdr)) || !write_all(s, cmdline, hdr.len)) { perror("write daemon"); - exit(1); + do_exit(1); } } @@ -90,7 +103,7 @@ void handle_input(int s) ret = read(local_stdout_fd, buf, sizeof(buf)); if (ret < 0) { perror("read"); - exit(1); + do_exit(1); } if (ret == 0) { local_stdout_fd = -1; @@ -98,7 +111,7 @@ void handle_input(int s) } if (!write_all(s, buf, ret)) { perror("write daemon"); - exit(1); + do_exit(1); } } @@ -110,15 +123,15 @@ void handle_daemon_data(int s) if (!read_all(s, &hdr, sizeof hdr)) { perror("read daemon"); - exit(1); + do_exit(1); } if (hdr.len > MAX_DATA_CHUNK) { fprintf(stderr, "client_header.len=%d\n", hdr.len); - exit(1); + do_exit(1); } if (!read_all(s, buf, hdr.len)) { perror("read daemon"); - exit(1); + do_exit(1); } switch (hdr.type) { @@ -127,7 +140,7 @@ void handle_daemon_data(int s) close(local_stdin_fd); else if (!write_all(local_stdin_fd, buf, hdr.len)) { perror("write local stdout"); - exit(1); + do_exit(1); } break; case MSG_SERVER_TO_CLIENT_STDERR: @@ -136,13 +149,13 @@ void handle_daemon_data(int s) case MSG_SERVER_TO_CLIENT_EXIT_CODE: status = *(unsigned int *) buf; if (WIFEXITED(status)) - exit(WEXITSTATUS(status)); + do_exit(WEXITSTATUS(status)); else - exit(255); + do_exit(255); break; default: fprintf(stderr, "unknown msg %d\n", hdr.type); - exit(1); + do_exit(1); } } @@ -160,7 +173,7 @@ void handle_daemon_only_until_writable(s) if (select(s + 1, &rdset, &wrset, NULL, NULL) < 0) { perror("select"); - exit(1); + do_exit(1); } if (FD_ISSET(s, &rdset)) handle_daemon_data(s); @@ -183,7 +196,7 @@ void select_loop(int s) } if (select(max + 1, &select_set, NULL, NULL, NULL) < 0) { perror("select"); - exit(1); + do_exit(1); } if (FD_ISSET(s, &select_set)) handle_daemon_data(s); From 15bab70eaed146cc5675bdc6aa496f4c871ad739 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 15:18:37 +0100 Subject: [PATCH 53/86] Handle pipe io in qrexec_agent properly Don't reopen pipe after each read - no need, and it could lose events. --- qrexec/qrexec_agent.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qrexec/qrexec_agent.c b/qrexec/qrexec_agent.c index 94cab149..3245d870 100644 --- a/qrexec/qrexec_agent.c +++ b/qrexec/qrexec_agent.c @@ -483,9 +483,12 @@ void handle_trigger_io() write_all_vchan_ext(&s_hdr, sizeof s_hdr); } } +// trigger_fd is nonblock - so no need to reopen +#if 0 close(trigger_fd); trigger_fd = open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK); +#endif } int main() From d40fb3a2e1ce10dce019fed54b4ab2748df5011a Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 16:11:05 +0100 Subject: [PATCH 54/86] Fifo semantics is hard to get right. Finally: we need to close the command pipe at EOF. --- qrexec/qrexec_agent.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/qrexec/qrexec_agent.c b/qrexec/qrexec_agent.c index 3245d870..c40d7159 100644 --- a/qrexec/qrexec_agent.c +++ b/qrexec/qrexec_agent.c @@ -469,10 +469,11 @@ void handle_trigger_io() { struct server_header s_hdr; char buf[5]; + int ret; s_hdr.clid = 0; s_hdr.len = 0; - if (read(trigger_fd, buf, 4) == 4) { + if ((ret = read(trigger_fd, buf, 4)) == 4) { buf[4] = 0; if (!strcmp(buf, "FCPR")) s_hdr.clid = QREXEC_EXECUTE_FILE_COPY; @@ -484,11 +485,12 @@ void handle_trigger_io() } } // trigger_fd is nonblock - so no need to reopen -#if 0 - close(trigger_fd); - trigger_fd = - open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK); -#endif +// 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() From 343e23d459ffc4e53005e23e642037c40668923a Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 16 Mar 2011 11:20:00 -0400 Subject: [PATCH 55/86] Version 1.4.1 --- version_dom0 | 2 +- version_vm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/version_dom0 b/version_dom0 index 25b22e06..347f5833 100644 --- a/version_dom0 +++ b/version_dom0 @@ -1 +1 @@ -1.3.16 +1.4.1 diff --git a/version_vm b/version_vm index 7962dcfd..347f5833 100644 --- a/version_vm +++ b/version_vm @@ -1 +1 @@ -1.3.13 +1.4.1 From e410ad52ba88c471086f49e053254a94f7b0e79f Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 16:24:54 +0100 Subject: [PATCH 56/86] Bloody perror messes with errno; need to save errno. --- common/ioall.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/common/ioall.c b/common/ioall.c index a99a8aac..239f3333 100644 --- a/common/ioall.c +++ b/common/ioall.c @@ -25,6 +25,14 @@ #include #include +void perror_wrapper(char * msg) +{ + int prev=errno; + perror(msg); + errno=prev; +} + + int write_all(int fd, void *buf, int size) { int written = 0; @@ -34,7 +42,7 @@ int write_all(int fd, void *buf, int size) if (ret == -1 && errno == EINTR) continue; if (ret <= 0) { - perror("write"); + perror_wrapper("write"); return 0; } written += ret; @@ -57,7 +65,7 @@ int read_all(int fd, void *buf, int size) return 0; } if (ret < 0) { - perror("read"); + perror_wrapper("read"); return 0; } got_read += ret; @@ -77,11 +85,11 @@ int copy_fd_all(int fdout, int fdin) if (!ret) break; if (ret < 0) { - perror("read"); + perror_wrapper("read"); return 0; } if (!write_all(fdout, buf, ret)) { - perror("write"); + perror_wrapper("write"); return 0; } } From 1892bef66f14c2a78cb15891887e620e1a690b65 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 16 Mar 2011 11:32:51 -0400 Subject: [PATCH 57/86] Require xen 3.4.3-6 with fixed /etc/xen/scripts/block --- rpm_spec/core-dom0.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index e770b66c..dd1ec6d6 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -40,6 +40,7 @@ BuildRequires: xen-devel Requires: python, xen-runtime, pciutils, python-inotify, python-daemon, kernel-qubes-dom0 Conflicts: qubes-gui-dom0 < 1.1.13 Requires: NetworkManager >= 0.8.1-1 +Requires: xen >= 3.4.3-6 %define _builddir %(pwd)/dom0 %description From 4087b1d052b5a9c85c8ca69f13654d24ce0a720d Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 16 Mar 2011 16:47:32 +0100 Subject: [PATCH 58/86] Package qvm-copy-to-vm2*, too. --- rpm_spec/core-appvm.spec | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index f3c875d4..7821292f 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -73,8 +73,10 @@ cp qubes_core $RPM_BUILD_ROOT/etc/init.d/ mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/usr/bin cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm qvm-open-in-dvm2 $RPM_BUILD_ROOT/usr/bin +cp qvm-copy-to-vm2 $RPM_BUILD_ROOT/usr/bin mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes +cp qvm-copy-to-vm2.kde $RPM_BUILD_ROOT/usr/lib/qubes cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes cp dvm_file_editor qfile-agent qfile-agent-dvm qfile-unpacker $RPM_BUILD_ROOT/usr/lib/qubes ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer @@ -205,7 +207,9 @@ rm -rf $RPM_BUILD_ROOT /etc/fstab /etc/init.d/qubes_core /usr/bin/qvm-copy-to-vm +/usr/bin/qvm-copy-to-vm2 /usr/lib/qubes/qvm-copy-to-vm.kde +/usr/lib/qubes/qvm-copy-to-vm2.kde %attr(4755,root,root) /usr/bin/qvm-open-in-dvm /usr/bin/qvm-open-in-dvm2 /usr/lib/qubes/qvm-dvm-transfer From 379a5620c86273d2fc05b31572bdcea5a2834351 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 16 Mar 2011 13:38:16 -0400 Subject: [PATCH 59/86] Fix netvm creation from template Missing netvms_conf_file parameter in template --- dom0/qvm-core/qubes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index a93a1e55..65c56b75 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -613,6 +613,7 @@ class QubesTemplateVm(QubesVm): root_img = kwargs.pop("root_img") if "root_img" in kwargs else None private_img = kwargs.pop("private_img") if "private_img" in kwargs else None appvms_conf_file = kwargs.pop("appvms_conf_file") if "appvms_conf_file" in kwargs else None + netvms_conf_file = kwargs.pop("netvms_conf_file") if "netvms_conf_file" in kwargs else None super(QubesTemplateVm, self).__init__(label = default_template_label, **kwargs) From ef6a3e576b3cd591821792e16adf47de02bdf1e3 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 16 Mar 2011 13:39:54 -0400 Subject: [PATCH 60/86] Parse tags %MEM% and %VCPUS% in {app,net}vm-template.conf (#115) --- dom0/qvm-core/qubes.py | 35 +++++++++++++++++++++++++++++++++-- dom0/qvm-tools/qvm-create | 11 +++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 65c56b75..13722c18 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -68,6 +68,8 @@ default_templatevm_conf_template = "templatevm.conf" # needed for TemplateVM clo default_appmenus_templates_subdir = "apps.templates" default_kernels_subdir = "kernels" default_firewall_conf_file = "firewall.xml" +default_memory = 400 +default_servicevm_vcpus = 1 # do not allow to start a new AppVM if Dom0 mem was to be less than this dom0_min_memory = 700*1024*1024 @@ -174,7 +176,9 @@ class QubesVm(object): netvm_vm = None, installed_by_rpm = False, updateable = False, - label = None): + label = None, + memory = default_memory, + vcpus = None): assert qid < qubes_max_qid, "VM id out of bounds!" @@ -209,6 +213,15 @@ class QubesVm(object): # PCI devices - used only by NetVM self.pcidevs = "" + self.memory = memory + + # By default allow use all VCPUs + if vcpus is None: + qubes_host = QubesHost() + self.vcpus = qubes_host.no_cpus + else: + self.vcpus = vcpus + if not dry_run and xend_session.session is not None: self.refresh_xend_session() @@ -685,9 +698,13 @@ class QubesTemplateVm(QubesVm): conf_templatevm_template = open (src_template_vm.templatevm_conf_template, "r") conf_file = open(self.conf_file, "w") rx_templatename = re.compile (r"%TEMPLATENAME%") + rx_mem = re.compile (r"%MEM%") + rx_vcpus = re.compile (r"%VCPUS%") for line in conf_templatevm_template: line = rx_templatename.sub (self.name, line) + line = rx_mem.sub (str(self.memory), line) + line = rx_vcpus.sub (str(self.vcpus), line) conf_file.write(line) conf_templatevm_template.close() @@ -906,12 +923,16 @@ class QubesCowVm(QubesVm): rx_vmdir = re.compile (r"%VMDIR%") rx_template = re.compile (r"%TEMPLATEDIR%") rx_pcidevs = re.compile (r"%PCIDEVS%") + rx_mem = re.compile (r"%MEM%") + rx_vcpus = re.compile (r"%VCPUS%") for line in conf_template: line = rx_vmname.sub (self.name, line) line = rx_vmdir.sub (self.dir_path, line) line = rx_template.sub (self.template_vm.dir_path, line) line = rx_pcidevs.sub (self.pcidevs, line) + line = rx_mem.sub (str(self.memory), line) + line = rx_vcpus.sub (str(self.vcpus), line) conf_appvm.write(line) conf_template.close() @@ -1036,6 +1057,9 @@ class QubesNetVm(QubesCowVm): if "label" not in kwargs or kwargs["label"] is None: kwargs["label"] = default_servicevm_label + + if "vcpus" not in kwargs or kwargs["vcpus"] is None: + kwargs["vcpus"] = default_servicevm_vcpus super(QubesNetVm, self).__init__(**kwargs) @property @@ -1117,6 +1141,8 @@ class QubesNetVm(QubesCowVm): private_img=self.private_img, installed_by_rpm=str(self.installed_by_rpm), label=self.label.name, + memory=str(self.memory), + vcpus=str(self.vcpus), ) return element @@ -1262,6 +1288,8 @@ class QubesProxyVm(QubesNetVm): private_img=self.private_img, installed_by_rpm=str(self.installed_by_rpm), label=self.label.name, + memory=str(self.memory), + vcpus=str(self.vcpus), ) return element @@ -1507,7 +1535,10 @@ class QubesAppVm(QubesCowVm): private_img=self.private_img, installed_by_rpm=str(self.installed_by_rpm), updateable=str(self.updateable), - label=self.label.name) + label=self.label.name, + memory=str(self.memory), + vcpus=str(self.vcpus), + ) return element def start(self, debug_console = False, verbose = False, preparing_dvm = False): diff --git a/dom0/qvm-tools/qvm-create b/dom0/qvm-tools/qvm-create index a16f19ba..efdf7aae 100755 --- a/dom0/qvm-tools/qvm-create +++ b/dom0/qvm-tools/qvm-create @@ -57,6 +57,10 @@ def main(): help="Create ProxyVM") parser.add_option ("-n", "--net", action="store_true", dest="netvm", default=False, help="Create NetVM") + parser.add_option ("-m", "--mem", dest="mem", default=None, + help="Initial memory size (in MB)") + parser.add_option ("-c", "--vcpus", dest="vcpus", default=None, + help="VCPUs count") parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True) (options, args) = parser.parse_args () @@ -127,6 +131,13 @@ def main(): vm = qvm_collection.add_new_proxyvm(vmname, template_vm, label = label) else: vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label) + + if options.mem is not None: + vm.memory = options.mem + + if options.vcpus is not None: + vm.vcpus = options.vcpus + try: vm.create_on_disk(verbose=options.verbose) vm.add_to_xen_storage() From 4e68c4cde9aaa79acbd3b7c6810af7ce314f72d5 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 16 Mar 2011 18:45:02 -0400 Subject: [PATCH 61/86] Standalone VM (#98) 'updateable' property is now read-onlyr; updateable=True means that VM has own root.img, not persistent root-cow.img. --- dom0/qvm-core/qubes.py | 199 ++++++++++++++++++++++---------------- dom0/qvm-tools/qvm-create | 8 +- dom0/qvm-tools/qvm-ls | 8 +- 3 files changed, 128 insertions(+), 87 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 13722c18..f5649a36 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -64,6 +64,7 @@ default_swapcow_img = "swap-cow.img" default_private_img = "private.img" default_appvms_conf_file = "appvm-template.conf" default_netvms_conf_file = "netvm-template.conf" +default_standalonevms_conf_file = "standalone-template.conf" default_templatevm_conf_template = "templatevm.conf" # needed for TemplateVM cloning default_appmenus_templates_subdir = "apps.templates" default_kernels_subdir = "kernels" @@ -177,6 +178,8 @@ class QubesVm(object): installed_by_rpm = False, updateable = False, label = None, + root_img = None, + private_img = None, memory = default_memory, vcpus = None): @@ -203,6 +206,21 @@ class QubesVm(object): # We use it in remove from disk to avoid removing rpm files (for templates) self.installed_by_rpm = installed_by_rpm + # Setup standard VM storage; some VM types may not use them all + if root_img is not None and os.path.isabs(root_img): + self.root_img = root_img + else: + self.root_img = dir_path + "/" + ( + root_img if root_img is not None else default_root_img) + + self.rootcow_img = dir_path + "/" + default_rootcow_img + + if private_img is not None and os.path.isabs(private_img): + self.private_img = private_img + else: + self.private_img = dir_path + "/" + ( + private_img if private_img is not None else default_private_img) + self.updateable = updateable self.label = label if label is not None else QubesVmLabels["red"] if self.dir_path is not None: @@ -399,6 +417,18 @@ class QubesVm(object): p = 100 return p + def get_disk_utilization_root_img(self): + if not os.path.exists(self.root_img): + return 0 + + return self.get_disk_usage(self.root_img) + + def get_root_img_sz(self): + if not os.path.exists(self.root_img): + return 0 + + return os.path.getsize(self.root_img) + def get_power_state(self): if dry_run: return "NA" @@ -623,29 +653,13 @@ class QubesTemplateVm(QubesVm): if "updateable" not in kwargs or kwargs["updateable"] is None : kwargs["updateable"] = True - root_img = kwargs.pop("root_img") if "root_img" in kwargs else None - private_img = kwargs.pop("private_img") if "private_img" in kwargs else None appvms_conf_file = kwargs.pop("appvms_conf_file") if "appvms_conf_file" in kwargs else None netvms_conf_file = kwargs.pop("netvms_conf_file") if "netvms_conf_file" in kwargs else None + standalonevms_conf_file = kwargs.pop("standalonevms_conf_file") if "standalonevms_conf_file" in kwargs else None super(QubesTemplateVm, self).__init__(label = default_template_label, **kwargs) dir_path = kwargs["dir_path"] - - if root_img is not None and os.path.isabs(root_img): - self.root_img = root_img - else: - self.root_img = dir_path + "/" + ( - root_img if root_img is not None else default_root_img) - - self.rootcow_img = dir_path + "/" + default_rootcow_img - - if private_img is not None and os.path.isabs(private_img): - self.private_img = private_img - else: - self.private_img = dir_path + "/" + ( - private_img if private_img is not None else default_private_img) - if appvms_conf_file is not None and os.path.isabs(appvms_conf_file): self.appvms_conf_file = appvms_conf_file else: @@ -658,6 +672,12 @@ class QubesTemplateVm(QubesVm): self.netvms_conf_file = dir_path + "/" + ( netvms_conf_file if netvms_conf_file is not None else default_netvms_conf_file) + if standalonevms_conf_file is not None and os.path.isabs(standalonevms_conf_file): + self.standalonevms_conf_file = standalonevms_conf_file + else: + self.standalonevms_conf_file = dir_path + "/" + ( + standalonevms_conf_file if standalonevms_conf_file is not None else default_standalonevms_conf_file) + self.templatevm_conf_template = self.dir_path + "/" + default_templatevm_conf_template self.kernels_dir = self.dir_path + "/" + default_kernels_subdir self.appmenus_templates_dir = self.dir_path + "/" + default_appmenus_templates_subdir @@ -725,6 +745,11 @@ class QubesTemplateVm(QubesVm): format(src_template_vm.netvms_conf_file, self.netvms_conf_file) shutil.copy (src_template_vm.netvms_conf_file, self.netvms_conf_file) + if verbose: + print "--> Copying the VM config template :\n{0} ==>\n{1}".\ + format(src_template_vm.standalonevms_conf_file, self.standalonevms_conf_file) + shutil.copy (src_template_vm.standalonevms_conf_file, self.standalonevms_conf_file) + if verbose: print "--> Copying the template's private image:\n{0} ==>\n{1}".\ format(src_template_vm.private_img, self.private_img) @@ -761,15 +786,6 @@ class QubesTemplateVm(QubesVm): shutil.copytree (src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) - def get_disk_utilization_root_img(self): - return self.get_disk_usage(self.root_img) - - def get_root_img_sz(self): - if not os.path.exists(self.root_img): - return 0 - - return os.path.getsize(self.root_img) - def verify_files(self): if dry_run: return @@ -847,6 +863,7 @@ class QubesTemplateVm(QubesVm): conf_file=self.conf_file, appvms_conf_file=self.appvms_conf_file, netvms_conf_file=self.netvms_conf_file, + standalonevms_conf_file=self.standalonevms_conf_file, root_img=self.root_img, rootcow_img=self.rootcow_img, private_img=self.private_img, @@ -859,7 +876,8 @@ class QubesTemplateVm(QubesVm): class QubesCowVm(QubesVm): """ - A class that represent a VM based on some template, i.e. doesn't have own root.img + A class that represent a VM that may be based on some template, i.e. doesn't have own root.img + """ def __init__(self, **kwargs): if "dir_path" not in kwargs or kwargs["dir_path"] is None: @@ -868,52 +886,40 @@ class QubesCowVm(QubesVm): if "updateable" not in kwargs or kwargs["updateable"] is None: kwargs["updateable"] = False - private_img = kwargs.pop("private_img") template_vm = kwargs.pop("template_vm") super(QubesCowVm, self).__init__(**kwargs) qid = kwargs["qid"] dir_path = kwargs["dir_path"] - # Dirty hack for QubesDom0NetVm... - if not isinstance(self, QubesDom0NetVm): - assert template_vm is not None, "Missing template_vm for template based VM!" - if not template_vm.is_template(): - print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\ - format(template_vm.qid) - return False + if not self.is_updateable(): + # Dirty hack for QubesDom0NetVm... + if not isinstance(self, QubesDom0NetVm): + assert template_vm is not None, "Missing template_vm for template based VM!" + if not template_vm.is_template(): + print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\ + format(template_vm.qid) + return False + + template_vm.appvms[qid] = self + else: + assert self.root_img is not None, "Missing root_img for standalone VM!" - template_vm.appvms[qid] = self self.template_vm = template_vm - # template based VM doesn't have its own root_img, it uses the one provided by the TemplateVM - if private_img is not None and os.path.isabs(private_img): - self.private_img = private_img - else: - self.private_img = dir_path + "/" + ( - private_img if private_img is not None else default_private_img) - - self.rootcow_img = dir_path + "/" + default_rootcow_img self.swapcow_img = dir_path + "/" + default_swapcow_img def set_updateable(self): if self.is_updateable(): return - assert not self.is_running() - # Check if the TemaplteVM is *non* updatable... - if not self.template_vm.is_updateable(): - self.updateable = True - self.reset_cow_storage() - self.reset_swap_cow_storage() - else: - # Temaplate VM is Updatable itself --> can't make the AppVM updateable too - # as this would cause COW-backed storage incoherency - raise QubesException ("TemaplteVM is updateable: cannot make the template based VM '{0}' updateable".format(self.name)) + raise QubesException ("Change 'updateable' flag is not supported. Please use qvm-create.") def create_config_file(self): conf_template = None if self.type == "NetVM": conf_template = open (self.template_vm.netvms_conf_file, "r") + elif self.updateable: + conf_template = open (self.template_vm.standalonevms_conf_file, "r") else: conf_template = open (self.template_vm.appvms_conf_file, "r") if os.path.isfile(self.conf_file): @@ -962,11 +968,17 @@ class QubesCowVm(QubesVm): raise IOError ("Error while copying {0} to {1}".\ format(template_priv, self.private_img)) - def get_disk_utilization_root_img(self): - return 0 + if self.is_updateable(): + template_root = self.template_vm.root_img + if verbose: + print "--> Copying the template's root image: {0}".\ + format(template_root) - def get_root_img_sz(self): - return 0 + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", template_root, self.root_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(template_root, self.root_img)) def verify_files(self): if dry_run: @@ -982,6 +994,11 @@ class QubesCowVm(QubesVm): "VM config file doesn't exist: {0}".\ format(self.conf_file)) + if self.is_updateable() and not os.path.exists (self.root_img): + raise QubesException ( + "VM root image file doesn't exist: {0}".\ + format(self.root_img)) + if not os.path.exists (self.private_img): raise QubesException ( "VM private image file doesn't exist: {0}".\ @@ -1136,8 +1153,9 @@ class QubesNetVm(QubesCowVm): name=self.name, dir_path=self.dir_path, conf_file=self.conf_file, - template_qid=str(self.template_vm.qid), + template_qid=str(self.template_vm.qid) if self.template_vm is not None else "none", updateable=str(self.updateable), + root_img=self.root_img, private_img=self.private_img, installed_by_rpm=str(self.installed_by_rpm), label=self.label.name, @@ -1282,9 +1300,10 @@ class QubesProxyVm(QubesNetVm): name=self.name, dir_path=self.dir_path, conf_file=self.conf_file, - template_qid=str(self.template_vm.qid), + template_qid=str(self.template_vm.qid) if self.template_vm is not None else "none", updateable=str(self.updateable), netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", + root_img=self.root_img, private_img=self.private_img, installed_by_rpm=str(self.installed_by_rpm), label=self.label.name, @@ -1422,10 +1441,6 @@ class QubesAppVm(QubesCowVm): def type(self): return "AppVM" - def set_updateable(self): - - super(QubesAppVm, self).set_updateable() - def create_on_disk(self, verbose): if dry_run: return @@ -1529,9 +1544,10 @@ class QubesAppVm(QubesCowVm): name=self.name, dir_path=self.dir_path, conf_file=self.conf_file, - template_qid=str(self.template_vm.qid), + template_qid=str(self.template_vm.qid) if self.template_vm is not None else "none", uses_default_netvm=str(self.uses_default_netvm), netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", + root_img=self.root_img, private_img=self.private_img, installed_by_rpm=str(self.installed_by_rpm), updateable=str(self.updateable), @@ -1585,6 +1601,7 @@ class QubesVmCollection(dict): def add_new_appvm(self, name, template_vm, dir_path = None, conf_file = None, private_img = None, + updateable = False, label = None): qid = self.get_new_unused_qid() @@ -1592,6 +1609,7 @@ class QubesVmCollection(dict): dir_path=dir_path, conf_file=conf_file, private_img=private_img, netvm_vm = self.get_default_netvm_vm(), + updateable=updateable, label=label) if not self.verify_new_vm (vm): @@ -1645,13 +1663,14 @@ class QubesVmCollection(dict): def add_new_netvm(self, name, template_vm, dir_path = None, conf_file = None, private_img = None, installed_by_rpm = False, - label = None): + label = None, updateable = False): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() vm = QubesNetVm (qid=qid, name=name, template_vm=template_vm, netid=netid, label=label, private_img=private_img, installed_by_rpm=installed_by_rpm, + updateable=updateable, dir_path=dir_path, conf_file=conf_file) if not self.verify_new_vm (vm): @@ -1666,7 +1685,7 @@ class QubesVmCollection(dict): def add_new_proxyvm(self, name, template_vm, dir_path = None, conf_file = None, private_img = None, installed_by_rpm = False, - label = None): + label = None, updateable = False): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() @@ -1674,6 +1693,7 @@ class QubesVmCollection(dict): netid=netid, label=label, private_img=private_img, installed_by_rpm=installed_by_rpm, dir_path=dir_path, conf_file=conf_file, + updateable=updateable, netvm_vm = self.get_default_fw_netvm_vm()) if not self.verify_new_vm (vm): @@ -1863,7 +1883,7 @@ class QubesVmCollection(dict): kwargs = {} attr_list = ("qid", "name", "dir_path", "conf_file", - "appvms_conf_file", "appvms_conf_file", + "appvms_conf_file", "netvms_conf_file", "standalonevms_conf_file", "private_img", "root_img", "installed_by_rpm", "updateable", "uses_default_netvm") @@ -1891,20 +1911,25 @@ class QubesVmCollection(dict): kwargs = {} attr_list = ("qid", "netid", "name", "dir_path", "conf_file", "private_img", "template_qid", "updateable", "label", + "root_img", ) for attribute in attr_list: kwargs[attribute] = element.get(attribute) kwargs["qid"] = int(kwargs["qid"]) - kwargs["template_qid"] = int(kwargs["template_qid"]) if kwargs["updateable"] is not None: kwargs["updateable"] = True if kwargs["updateable"] == "True" else False - template_vm = self[kwargs.pop("template_qid")] - if template_vm is None: - print "ERROR: NetVM '{0}' uses unkown template qid='{1}'!".\ - format(kwargs["name"], kwargs["template_qid"]) + template_vm = None + if kwargs["updateable"] == False: + kwargs["template_qid"] = int(kwargs["template_qid"]) + template_vm = self[kwargs.pop("template_qid")] + if template_vm is None: + print "ERROR: NetVM '{0}' uses unkown template qid='{1}'!".\ + format(kwargs["name"], kwargs["template_qid"]) + else: + kwargs.pop("template_qid") kwargs["template_vm"] = template_vm kwargs["netid"] = int(kwargs["netid"]) @@ -1936,14 +1961,18 @@ class QubesVmCollection(dict): kwargs[attribute] = element.get(attribute) kwargs["qid"] = int(kwargs["qid"]) - kwargs["template_qid"] = int(kwargs["template_qid"]) if kwargs["updateable"] is not None: kwargs["updateable"] = True if kwargs["updateable"] == "True" else False - template_vm = self[kwargs.pop("template_qid")] - if template_vm is None: - print "ERROR: ProxyVM '{0}' uses unkown template qid='{1}'!".\ - format(kwargs["name"], kwargs["template_qid"]) + template_vm = None + if kwargs["updateable"] == False: + kwargs["template_qid"] = int(kwargs["template_qid"]) + template_vm = self[kwargs.pop("template_qid")] + if template_vm is None: + print "ERROR: ProxyVM '{0}' uses unkown template qid='{1}'!".\ + format(kwargs["name"], kwargs["template_qid"]) + else: + kwargs.pop("template_qid") kwargs["template_vm"] = template_vm kwargs["netid"] = int(kwargs["netid"]) @@ -2042,14 +2071,18 @@ class QubesVmCollection(dict): kwargs[attribute] = element.get(attribute) kwargs["qid"] = int(kwargs["qid"]) - kwargs["template_qid"] = int(kwargs["template_qid"]) if kwargs["updateable"] is not None: kwargs["updateable"] = True if kwargs["updateable"] == "True" else False - template_vm = self[kwargs.pop("template_qid")] - if template_vm is None: - print "ERROR: AppVM '{0}' uses unkown template qid='{1}'!".\ - format(kwargs["name"], kwargs["template_qid"]) + template_vm = None + if kwargs["updateable"] == False: + kwargs["template_qid"] = int(kwargs["template_qid"]) + template_vm = self[kwargs.pop("template_qid")] + if template_vm is None: + print "ERROR: AppVM '{0}' uses unkown template qid='{1}'!".\ + format(kwargs["name"], kwargs["template_qid"]) + else: + kwargs.pop("template_qid") kwargs["template_vm"] = template_vm diff --git a/dom0/qvm-tools/qvm-create b/dom0/qvm-tools/qvm-create index efdf7aae..c4c80b97 100755 --- a/dom0/qvm-tools/qvm-create +++ b/dom0/qvm-tools/qvm-create @@ -57,6 +57,8 @@ def main(): help="Create ProxyVM") parser.add_option ("-n", "--net", action="store_true", dest="netvm", default=False, help="Create NetVM") + parser.add_option ("-s", "--standalone", action="store_true", dest="standalone", default=False, + help="Create standalone VM - independent of template ") parser.add_option ("-m", "--mem", dest="mem", default=None, help="Initial memory size (in MB)") parser.add_option ("-c", "--vcpus", dest="vcpus", default=None, @@ -115,7 +117,7 @@ def main(): vm = None if options.netvm: - vm = qvm_collection.add_new_netvm(vmname, template_vm, label = label) + vm = qvm_collection.add_new_netvm(vmname, template_vm, label = label, updateable = options.standalone) net_devices = find_net_devices() print "Found the following net devices in your system:" @@ -128,9 +130,9 @@ def main(): vm.pcidevs = dev_str elif options.proxyvm: - vm = qvm_collection.add_new_proxyvm(vmname, template_vm, label = label) + vm = qvm_collection.add_new_proxyvm(vmname, template_vm, label = label, updateable = options.standalone) else: - vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label) + vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label, updateable = options.standalone) if options.mem is not None: vm.memory = options.mem diff --git a/dom0/qvm-tools/qvm-ls b/dom0/qvm-tools/qvm-ls index 14d3e95f..a5b4c006 100755 --- a/dom0/qvm-tools/qvm-ls +++ b/dom0/qvm-tools/qvm-ls @@ -141,12 +141,18 @@ def main(): if netvm.is_netvm(): vms_to_display.append (netvm) + # Now, the AppVMs without template... + for appvm in vms_list: + if appvm.is_appvm() and appvm.template_vm is None: + vms_to_display.append (appvm) + # Now, the template, and all its AppVMs... for tvm in vms_list: if tvm.is_template(): vms_to_display.append (tvm) for vm in vms_list: - if (vm.is_appvm() or vm.is_disposablevm()) and vm.template_vm.qid == tvm.qid: + if (vm.is_appvm() or vm.is_disposablevm()) and \ + vm.template_vm and vm.template_vm.qid == tvm.qid: vms_to_display.append(vm) assert len(vms_to_display) == no_vms From bef1ea4c92d67c38adbec5944282e315dc5666a0 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 16 Mar 2011 19:42:01 -0400 Subject: [PATCH 62/86] Reduce duplicated code in create_xml_entries --- dom0/qvm-core/qubes.py | 140 +++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 84 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index f5649a36..d7fa96df 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -640,6 +640,34 @@ class QubesVm(object): shutil.rmtree (self.dir_path) + def get_xml_attrs(self): + attrs = {} + attrs["qid"] = str(self.qid) + attrs["name"] = self.name + attrs["dir_path"] = self.dir_path + attrs["conf_file"] = self.conf_file + attrs["root_img"] = self.root_img + attrs["rootcow_img"] = self.rootcow_img + attrs["private_img"] = self.private_img + attrs["uses_default_netvm"] = str(self.uses_default_netvm) + attrs["netvm_qid"] = str(self.netvm_vm.qid) if self.netvm_vm is not None else "none" + attrs["installed_by_rpm"] = str(self.installed_by_rpm) + attrs["updateable"] = str(self.updateable) + attrs["label"] = self.label.name + attrs["memory"] = str(self.memory) + attrs["vcpus"] = str(self.vcpus) + return attrs + + def create_xml_element(self): + # Compatibility hack (Qubes*VM in type vs Qubes*Vm in XML)... + rx_type = re.compile (r"VM") + + attrs = self.get_xml_attrs() + element = xml.etree.ElementTree.Element( + "Qubes" + rx_type.sub("Vm", self.type), + **attrs) + return element + class QubesTemplateVm(QubesVm): """ @@ -854,25 +882,12 @@ class QubesTemplateVm(QubesVm): f_cow.close () f_root.close() - def create_xml_element(self): - element = xml.etree.ElementTree.Element( - "QubesTemplateVm", - qid=str(self.qid), - name=self.name, - dir_path=self.dir_path, - conf_file=self.conf_file, - appvms_conf_file=self.appvms_conf_file, - netvms_conf_file=self.netvms_conf_file, - standalonevms_conf_file=self.standalonevms_conf_file, - root_img=self.root_img, - rootcow_img=self.rootcow_img, - private_img=self.private_img, - uses_default_netvm=str(self.uses_default_netvm), - netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", - installed_by_rpm=str(self.installed_by_rpm), - updateable=str(self.updateable), - ) - return element + def get_xml_attrs(self): + attrs = super(QubesTemplateVm, self).get_xml_attrs() + attrs["appvms_conf_file"] = self.appvms_conf_file + attrs["netvms_conf_file"] = self.netvms_conf_file + attrs["standalonevms_conf_file"] = self.standalonevms_conf_file + return attrs class QubesCowVm(QubesVm): """ @@ -1055,6 +1070,11 @@ class QubesCowVm(QubesVm): subprocess.check_call ([qubes_appmenu_remove_cmd, self.name]) shutil.rmtree (self.dir_path) + def get_xml_attrs(self): + attrs = super(QubesCowVm, self).get_xml_attrs() + attrs["template_qid"] = str(self.template_vm.qid) if self.template_vm is not None else "none" + return attrs + class QubesNetVm(QubesCowVm): """ A class that represents a NetVM. A child of QubesCowVM. @@ -1145,24 +1165,12 @@ class QubesNetVm(QubesCowVm): self.__external_ip_allowed_xids.discard(int(xid)) self.update_external_ip_permissions() - def create_xml_element(self): - element = xml.etree.ElementTree.Element( - "QubesNetVm", - qid=str(self.qid), - netid=str(self.netid), - name=self.name, - dir_path=self.dir_path, - conf_file=self.conf_file, - template_qid=str(self.template_vm.qid) if self.template_vm is not None else "none", - updateable=str(self.updateable), - root_img=self.root_img, - private_img=self.private_img, - installed_by_rpm=str(self.installed_by_rpm), - label=self.label.name, - memory=str(self.memory), - vcpus=str(self.vcpus), - ) - return element + def get_xml_attrs(self): + attrs = super(QubesNetVm, self).get_xml_attrs() + attrs.pop("netvm_qid") + attrs.pop("uses_default_netvm") + attrs["netid"] = str(self.netid) + return attrs class QubesProxyVm(QubesNetVm): """ @@ -1292,25 +1300,10 @@ class QubesProxyVm(QubesNetVm): "/local/domain/{0}/qubes_iptables".format(self.get_xid()), iptables]) - def create_xml_element(self): - element = xml.etree.ElementTree.Element( - "QubesProxyVm", - qid=str(self.qid), - netid=str(self.netid), - name=self.name, - dir_path=self.dir_path, - conf_file=self.conf_file, - template_qid=str(self.template_vm.qid) if self.template_vm is not None else "none", - updateable=str(self.updateable), - netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", - root_img=self.root_img, - private_img=self.private_img, - installed_by_rpm=str(self.installed_by_rpm), - label=self.label.name, - memory=str(self.memory), - vcpus=str(self.vcpus), - ) - return element + def get_xml_attrs(self): + attrs = super(QubesProxyVm, self).get_xml_attrs() + attrs["netvm_qid"] = str(self.netvm_vm.qid) if self.netvm_vm is not None else "none" + return attrs class QubesDom0NetVm(QubesNetVm): def __init__(self): @@ -1410,14 +1403,13 @@ class QubesDisposableVm(QubesVm): def type(self): return "DisposableVM" - def create_xml_element(self): - element = xml.etree.ElementTree.Element( - "QubesDisposableVm", - qid=str(self.qid), - name=self.name, - template_qid=str(self.template_vm.qid), - label=self.label.name) - return element + def get_xml_attrs(self): + attrs = {} + attrs["qid"] = str(self.qid) + attrs["name"] = self.name + attrs["template_qid"] = str(self.template_vm.qid) + attrs["label"] = self.label.name + return attrs def verify_files(self): return True @@ -1537,26 +1529,6 @@ class QubesAppVm(QubesCowVm): return conf - def create_xml_element(self): - element = xml.etree.ElementTree.Element( - "QubesAppVm", - qid=str(self.qid), - name=self.name, - dir_path=self.dir_path, - conf_file=self.conf_file, - template_qid=str(self.template_vm.qid) if self.template_vm is not None else "none", - uses_default_netvm=str(self.uses_default_netvm), - netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", - root_img=self.root_img, - private_img=self.private_img, - installed_by_rpm=str(self.installed_by_rpm), - updateable=str(self.updateable), - label=self.label.name, - memory=str(self.memory), - vcpus=str(self.vcpus), - ) - return element - def start(self, debug_console = False, verbose = False, preparing_dvm = False): if dry_run: return From 33e7ee3623768c4df218c92c363ea1e4cae66531 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 16 Mar 2011 20:40:15 -0400 Subject: [PATCH 63/86] Reduce duplicated code in qubes.xml load Parse common attrs in separate function. Side effect: possibility to set custom TemplateVM label --- dom0/qvm-core/qubes.py | 260 +++++++++++++++-------------------------- 1 file changed, 92 insertions(+), 168 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index d7fa96df..3999d0a2 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -685,7 +685,10 @@ class QubesTemplateVm(QubesVm): netvms_conf_file = kwargs.pop("netvms_conf_file") if "netvms_conf_file" in kwargs else None standalonevms_conf_file = kwargs.pop("standalonevms_conf_file") if "standalonevms_conf_file" in kwargs else None - super(QubesTemplateVm, self).__init__(label = default_template_label, **kwargs) + if "label" not in kwargs or kwargs["label"] == None: + kwargs["label"] = default_template_label + + super(QubesTemplateVm, self).__init__(**kwargs) dir_path = kwargs["dir_path"] if appvms_conf_file is not None and os.path.isabs(appvms_conf_file): @@ -901,7 +904,10 @@ class QubesCowVm(QubesVm): if "updateable" not in kwargs or kwargs["updateable"] is None: kwargs["updateable"] = False - template_vm = kwargs.pop("template_vm") + if "template_vm" in kwargs: + template_vm = kwargs.pop("template_vm") + else: + template_vm = None super(QubesCowVm, self).__init__(**kwargs) qid = kwargs["qid"] @@ -1072,7 +1078,7 @@ class QubesCowVm(QubesVm): def get_xml_attrs(self): attrs = super(QubesCowVm, self).get_xml_attrs() - attrs["template_qid"] = str(self.template_vm.qid) if self.template_vm is not None else "none" + attrs["template_qid"] = str(self.template_vm.qid) if not self.is_updateable() else "none" return attrs class QubesNetVm(QubesCowVm): @@ -1177,7 +1183,8 @@ class QubesProxyVm(QubesNetVm): A class that represents a ProxyVM, ex FirewallVM. A child of QubesNetVM. """ def __init__(self, **kwargs): - super(QubesProxyVm, self).__init__(uses_default_netvm=False, **kwargs) + kwargs["uses_default_netvm"] = False + super(QubesProxyVm, self).__init__(**kwargs) self.rules_applied = None @property @@ -1813,6 +1820,73 @@ class QubesVmCollection(dict): return False return True + def parse_xml_element(self, element): + kwargs = {} + common_attr_list = ("qid", "name", "dir_path", "conf_file", + "private_img", "root_img", "template_qid", + "installed_by_rpm", "updateable", + "uses_default_netvm", "label") + + for attribute in common_attr_list: + kwargs[attribute] = element.get(attribute) + + kwargs["qid"] = int(kwargs["qid"]) + if kwargs["updateable"] is not None: + kwargs["updateable"] = True if kwargs["updateable"] == "True" else False + + if "installed_by_rpm" in kwargs: + kwargs["installed_by_rpm"] = True if kwargs["installed_by_rpm"] == "True" else False + + if "template_qid" in kwargs: + if kwargs["template_qid"] == "none" or kwargs["template_qid"] is None: + kwargs.pop("template_qid") + else: + kwargs["template_qid"] = int(kwargs["template_qid"]) + template_vm = self[kwargs.pop("template_qid")] + if template_vm is None: + print "ERROR: VM '{0}' uses unkown template qid='{1}'!".\ + format(kwargs["name"], kwargs["template_qid"]) + + kwargs["template_vm"] = template_vm + + if kwargs["label"] is not None: + if kwargs["label"] not in QubesVmLabels: + print "ERROR: incorrect label for VM '{0}'".format(kwargs["name"]) + kwargs.pop ("label") + else: + kwargs["label"] = QubesVmLabels[kwargs["label"]] + + return kwargs + + def set_netvm_dependency(self, element): + kwargs = {} + attr_list = ("qid", "uses_default_netvm", "netvm_qid") + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + vm = self[int(kwargs["qid"])] + + if "uses_default_netvm" not in kwargs: + vm.uses_default_netvm = True + else: + vm.uses_default_netvm = True if kwargs["uses_default_netvm"] == "True" else False + if vm.uses_default_netvm is True: + netvm_vm = self.get_default_netvm_vm() + kwargs.pop("netvm_qid") + else: + if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: + netvm_vm = None + kwargs.pop("netvm_qid") + else: + netvm_qid = int(kwargs.pop("netvm_qid")) + if netvm_qid not in self: + netvm_vm = None + else: + netvm_vm = self[netvm_qid] + + vm.netvm_vm = netvm_vm + def load(self): self.clear() @@ -1853,21 +1927,13 @@ class QubesVmCollection(dict): for element in tree.findall("QubesTemplateVm"): try: - kwargs = {} - attr_list = ("qid", "name", "dir_path", "conf_file", - "appvms_conf_file", "netvms_conf_file", "standalonevms_conf_file", - "private_img", "root_img", - "installed_by_rpm", "updateable", - "uses_default_netvm") + kwargs = self.parse_xml_element(element) + # Add TemplateVM specific fields + attr_list = ("appvms_conf_file", "netvms_conf_file", "standalonevms_conf_file") for attribute in attr_list: kwargs[attribute] = element.get(attribute) - kwargs["qid"] = int(kwargs["qid"]) - kwargs["installed_by_rpm"] = True if kwargs["installed_by_rpm"] == "True" else False - if kwargs["updateable"] is not None: - kwargs["updateable"] = True if kwargs["updateable"] == "True" else False - vm = QubesTemplateVm(**kwargs) self[vm.qid] = vm @@ -1880,39 +1946,15 @@ class QubesVmCollection(dict): # is needed to create all other VMs for element in tree.findall("QubesNetVm"): try: - kwargs = {} - attr_list = ("qid", "netid", "name", "dir_path", "conf_file", - "private_img", "template_qid", "updateable", "label", - "root_img", - ) + kwargs = self.parse_xml_element(element) + # Add NetVM specific fields + attr_list = ("netid",) for attribute in attr_list: kwargs[attribute] = element.get(attribute) - kwargs["qid"] = int(kwargs["qid"]) - if kwargs["updateable"] is not None: - kwargs["updateable"] = True if kwargs["updateable"] == "True" else False - - template_vm = None - if kwargs["updateable"] == False: - kwargs["template_qid"] = int(kwargs["template_qid"]) - template_vm = self[kwargs.pop("template_qid")] - if template_vm is None: - print "ERROR: NetVM '{0}' uses unkown template qid='{1}'!".\ - format(kwargs["name"], kwargs["template_qid"]) - else: - kwargs.pop("template_qid") - - kwargs["template_vm"] = template_vm kwargs["netid"] = int(kwargs["netid"]) - if kwargs["label"] is not None: - if kwargs["label"] not in QubesVmLabels: - print "ERROR: incorrect label for VM '{0}'".format(kwargs["name"]) - kwargs.pop ("label") - else: - kwargs["label"] = QubesVmLabels[kwargs["label"]] - vm = QubesNetVm(**kwargs) self[vm.qid] = vm @@ -1925,37 +1967,15 @@ class QubesVmCollection(dict): # by other VMs for element in tree.findall("QubesProxyVm"): try: - kwargs = {} - attr_list = ("qid", "netid", "name", "dir_path", "conf_file", "updateable", - "private_img", "template_qid", "label") + kwargs = self.parse_xml_element(element) + # Add ProxyVM specific fields + attr_list = ("netid",) for attribute in attr_list: kwargs[attribute] = element.get(attribute) - kwargs["qid"] = int(kwargs["qid"]) - if kwargs["updateable"] is not None: - kwargs["updateable"] = True if kwargs["updateable"] == "True" else False - - template_vm = None - if kwargs["updateable"] == False: - kwargs["template_qid"] = int(kwargs["template_qid"]) - template_vm = self[kwargs.pop("template_qid")] - if template_vm is None: - print "ERROR: ProxyVM '{0}' uses unkown template qid='{1}'!".\ - format(kwargs["name"], kwargs["template_qid"]) - else: - kwargs.pop("template_qid") - - kwargs["template_vm"] = template_vm kwargs["netid"] = int(kwargs["netid"]) - if kwargs["label"] is not None: - if kwargs["label"] not in QubesVmLabels: - print "ERROR: incorrect label for VM '{0}'".format(kwargs["name"]) - kwargs.pop ("label") - else: - kwargs["label"] = QubesVmLabels[kwargs["label"]] - vm = QubesProxyVm(**kwargs) self[vm.qid] = vm @@ -1968,35 +1988,7 @@ class QubesVmCollection(dict): # 1. For TemplateVMs for element in tree.findall("QubesTemplateVm"): try: - - kwargs = {} - attr_list = ("qid", "uses_default_netvm", "netvm_qid") - - for attribute in attr_list: - kwargs[attribute] = element.get(attribute) - - vm = self[int(kwargs["qid"])] - - if "uses_default_netvm" not in kwargs: - vm.uses_default_netvm = True - else: - vm.uses_default_netvm = True if kwargs["uses_default_netvm"] == "True" else False - if vm.uses_default_netvm is True: - netvm_vm = self.get_default_netvm_vm() - kwargs.pop("netvm_qid") - else: - if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: - netvm_vm = None - kwargs.pop("netvm_qid") - else: - netvm_qid = int(kwargs.pop("netvm_qid")) - if netvm_qid not in self: - netvm_vm = None - else: - netvm_vm = self[netvm_qid] - - vm.netvm_vm = netvm_vm - + self.set_netvm_dependency(element) except (ValueError, LookupError) as err: print("{0}: import error (QubesTemplateVm): {1}".format( os.path.basename(sys.argv[0]), err)) @@ -2005,26 +1997,7 @@ class QubesVmCollection(dict): # 2. For PoxyVMs for element in tree.findall("QubesProxyVm"): try: - kwargs = {} - attr_list = ("qid", "netvm_qid") - - for attribute in attr_list: - kwargs[attribute] = element.get(attribute) - - vm = self[int(kwargs["qid"])] - - if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: - netvm_vm = None - kwargs.pop("netvm_qid") - else: - netvm_qid = int(kwargs.pop("netvm_qid")) - if netvm_qid not in self: - netvm_vm = None - else: - netvm_vm = self[netvm_qid] - - vm.netvm_vm = netvm_vm - + self.set_netvm_dependency(element) except (ValueError, LookupError) as err: print("{0}: import error (QubesProxyVM) {1}".format( os.path.basename(sys.argv[0]), err)) @@ -2033,61 +2006,12 @@ class QubesVmCollection(dict): # Finally, read in the AppVMs for element in tree.findall("QubesAppVm"): try: - kwargs = {} - attr_list = ("qid", "name", "dir_path", "conf_file", - "private_img", "template_qid", - "updateable", "label", "netvm_qid", - "uses_default_netvm") - - for attribute in attr_list: - kwargs[attribute] = element.get(attribute) - - kwargs["qid"] = int(kwargs["qid"]) - if kwargs["updateable"] is not None: - kwargs["updateable"] = True if kwargs["updateable"] == "True" else False - - template_vm = None - if kwargs["updateable"] == False: - kwargs["template_qid"] = int(kwargs["template_qid"]) - template_vm = self[kwargs.pop("template_qid")] - if template_vm is None: - print "ERROR: AppVM '{0}' uses unkown template qid='{1}'!".\ - format(kwargs["name"], kwargs["template_qid"]) - else: - kwargs.pop("template_qid") - - kwargs["template_vm"] = template_vm - - if "uses_default_netvm" not in kwargs: - kwargs["uses_default_netvm"] = True - else: - kwargs["uses_default_netvm"] = True if kwargs["uses_default_netvm"] == "True" else False - if kwargs["uses_default_netvm"] is True: - netvm_vm = self.get_default_netvm_vm() - kwargs.pop("netvm_qid") - else: - if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: - netvm_vm = None - kwargs.pop("netvm_qid") - else: - netvm_qid = int(kwargs.pop("netvm_qid")) - if netvm_qid not in self: - netvm_vm = None - else: - netvm_vm = self[netvm_qid] - - kwargs["netvm_vm"] = netvm_vm - - if kwargs["label"] is not None: - if kwargs["label"] not in QubesVmLabels: - print "ERROR: incorrect label for VM '{0}'".format(kwargs["name"]) - kwargs.pop ("label") - else: - kwargs["label"] = QubesVmLabels[kwargs["label"]] - + kwargs = self.parse_xml_element(element) vm = QubesAppVm(**kwargs) self[vm.qid] = vm + + self.set_netvm_dependency(element) except (ValueError, LookupError) as err: print("{0}: import error (QubesAppVm): {1}".format( os.path.basename(sys.argv[0]), err)) From af7fefa73f3127467379caf5f27988bd3e5617b5 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Thu, 17 Mar 2011 16:53:29 +0100 Subject: [PATCH 64/86] qrexec: handle buffered writes correctly In case when we have a buffered write, always append to the buffer, even if the pipe happens to be writable now. If not, in case of certain tight race we might end up writing buffered data in wrong order. --- qrexec/write_stdin.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qrexec/write_stdin.c b/qrexec/write_stdin.c index 9eec24be..9da24bed 100644 --- a/qrexec/write_stdin.c +++ b/qrexec/write_stdin.c @@ -60,6 +60,12 @@ int write_stdin(int fd, int clid, char *data, int len, struct buffer *buffer) { int ret; + + if (buffer_len(buffer)) { + buffer_append(buffer, data, len); + return WRITE_STDIN_BUFFERED; + } + ret = write(fd, data, len); if (ret == len) return WRITE_STDIN_OK; From fb71bf968c246f8c0a9d4e7b4b44022ead82c402 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Thu, 17 Mar 2011 17:37:35 +0100 Subject: [PATCH 65/86] qrexec_agent: when receiving close from daemon, check buffered data We need to wait for buffer flush, so that buffered data is not lost, and only then close pipe to the child. --- qrexec/qrexec_agent.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/qrexec/qrexec_agent.c b/qrexec/qrexec_agent.c index c40d7159..8bc6e014 100644 --- a/qrexec/qrexec_agent.c +++ b/qrexec/qrexec_agent.c @@ -53,6 +53,7 @@ struct _client_info { int pid; int is_blocked; + int is_close_after_flush_needed; struct buffer buffer; }; @@ -176,6 +177,7 @@ void handle_exec(int clid, int len) client_info[clid].stderr_fd = stderr_fd; client_info[clid].pid = pid; client_info[clid].is_blocked = 0; + client_info[clid].is_close_after_flush_needed = 0; buffer_init(&client_info[clid].buffer); fprintf(stderr, "executed %s pid %d\n", buf, pid); @@ -242,8 +244,12 @@ void handle_input(int clid, int len) return; if (len == 0) { - close(client_info[clid].stdin_fd); - client_info[clid].stdin_fd = -1; + if (client_info[clid].is_blocked) + client_info[clid].is_close_after_flush_needed = 1; + else { + close(client_info[clid].stdin_fd); + client_info[clid].stdin_fd = -1; + } return; } @@ -453,6 +459,11 @@ void flush_client_data_agent(int clid) switch (flush_client_data(info->stdin_fd, clid, &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: remove_process(clid, 128); From 53b517f6a57a2cd5ba60910f62b5461281429d76 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Thu, 17 Mar 2011 17:53:33 +0100 Subject: [PATCH 66/86] qrexec: move set_nonblock function to write_stdin It will be needed there. --- qrexec/glue.h | 1 + qrexec/qrexec_agent.c | 6 ------ qrexec/qrexec_daemon.c | 6 ------ qrexec/write_stdin.c | 7 +++++++ 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/qrexec/glue.h b/qrexec/glue.h index 81c6c9e5..03bb6c15 100644 --- a/qrexec/glue.h +++ b/qrexec/glue.h @@ -44,3 +44,4 @@ enum { int flush_client_data(int fd, int clid, struct buffer *buffer); int write_stdin(int fd, int clid, char *data, int len, struct buffer *buffer); +void set_nonblock(int fd); diff --git a/qrexec/qrexec_agent.c b/qrexec/qrexec_agent.c index 8bc6e014..bace294f 100644 --- a/qrexec/qrexec_agent.c +++ b/qrexec/qrexec_agent.c @@ -143,12 +143,6 @@ void handle_just_exec(int clid, int len) fprintf(stderr, "executed (nowait) %s pid %d\n", buf, pid); } -void set_nonblock(int fd) -{ - int fl = fcntl(fd, F_GETFL, 0); - fcntl(fd, F_SETFL, fl | O_NONBLOCK); -} - void handle_exec(int clid, int len) { char buf[len]; diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index 8507c90f..051103ca 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -148,12 +148,6 @@ void pass_to_agent(int fd, struct server_header *s_hdr) write_all_vchan_ext(buf, len); } -void set_nonblock(int fd) -{ - int fl = fcntl(fd, F_GETFL, 0); - fcntl(fd, F_SETFL, fl | O_NONBLOCK); -} - void handle_client_cmdline(int fd) { struct client_header hdr; diff --git a/qrexec/write_stdin.c b/qrexec/write_stdin.c index 9da24bed..bb5a40a6 100644 --- a/qrexec/write_stdin.c +++ b/qrexec/write_stdin.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "qrexec.h" #include "buffer.h" #include "glue.h" @@ -89,3 +90,9 @@ int write_stdin(int fd, int clid, char *data, int len, } } + +void set_nonblock(int fd) +{ + int fl = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, fl | O_NONBLOCK); +} From 1d24ef9d1a041247bc01af0dceeac76869d9566e Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Thu, 17 Mar 2011 18:15:04 +0100 Subject: [PATCH 67/86] qrexec: when forgetting about a client/process, flush buffered data We need to spawn a child to take care of buffered data flushing, if there is any. Expensive, but should be needed rarely. --- qrexec/Makefile | 4 ++-- qrexec/glue.h | 1 + qrexec/qrexec_agent.c | 5 ++++- qrexec/qrexec_daemon.c | 6 +++++- qrexec/write_stdin.c | 32 +++++++++++++++++++++++++++++++- 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/qrexec/Makefile b/qrexec/Makefile index d4a5c857..58e48865 100644 --- a/qrexec/Makefile +++ b/qrexec/Makefile @@ -6,8 +6,8 @@ COMMONIOALL=../common/ioall.o all: qrexec_daemon qrexec_agent qrexec_client qrexec_daemon: qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o buffer.o write_stdin.o $(CC) -g -o qrexec_daemon qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o write_stdin.o buffer.o $(XENLIBS) -qrexec_agent: qrexec_agent.o exec.o txrx-vchan.o write_stdin.o buffer.o - $(CC) -g -o qrexec_agent qrexec_agent.o exec.o txrx-vchan.o write_stdin.o buffer.o $(XENLIBS) +qrexec_agent: qrexec_agent.o exec.o txrx-vchan.o write_stdin.o buffer.o $(COMMONIOALL) + $(CC) -g -o qrexec_agent qrexec_agent.o exec.o txrx-vchan.o write_stdin.o buffer.o $(COMMONIOALL) $(XENLIBS) qrexec_client: qrexec_client.o $(COMMONIOALL) exec.o $(CC) -g -o qrexec_client qrexec_client.o $(COMMONIOALL) exec.o clean: diff --git a/qrexec/glue.h b/qrexec/glue.h index 03bb6c15..60d697ad 100644 --- a/qrexec/glue.h +++ b/qrexec/glue.h @@ -45,3 +45,4 @@ int flush_client_data(int fd, int clid, struct buffer *buffer); int write_stdin(int fd, int clid, char *data, int len, struct buffer *buffer); void set_nonblock(int fd); +int fork_and_flush_stdin(int fd, struct buffer *buffer); diff --git a/qrexec/qrexec_agent.c b/qrexec/qrexec_agent.c index bace294f..24683c71 100644 --- a/qrexec/qrexec_agent.c +++ b/qrexec/qrexec_agent.c @@ -206,8 +206,11 @@ void remove_process(int clid, int status) int i; if (!client_info[clid].pid) return; + fork_and_flush_stdin(client_info[clid].stdin_fd, &client_info[clid].buffer); +#if 0 +// let's let it die by itself, possibly after it has received buffered stdin kill(client_info[clid].pid, SIGKILL); - +#endif if (status != -1) send_exit_code(clid, status); diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c index 051103ca..518ba4e7 100644 --- a/qrexec/qrexec_daemon.c +++ b/qrexec/qrexec_daemon.c @@ -118,10 +118,15 @@ void handle_new_client() max_client_fd = fd; } +int children_count; + void flush_client(int fd) { int i; struct server_header s_hdr; + + if (fork_and_flush_stdin(fd, &clients[fd].buffer)) + children_count++; close(fd); clients[fd].state = CLIENT_INVALID; buffer_free(&clients[fd].buffer); @@ -249,7 +254,6 @@ void pass_to_client(int clid, struct client_header *hdr) } } -int children_count; int child_exited; void sigchld_handler(int x) diff --git a/qrexec/write_stdin.c b/qrexec/write_stdin.c index bb5a40a6..8ac1b221 100644 --- a/qrexec/write_stdin.c +++ b/qrexec/write_stdin.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "qrexec.h" #include "buffer.h" #include "glue.h" @@ -66,7 +68,7 @@ int write_stdin(int fd, int clid, char *data, int len, buffer_append(buffer, data, len); return WRITE_STDIN_BUFFERED; } - + ret = write(fd, data, len); if (ret == len) return WRITE_STDIN_OK; @@ -96,3 +98,31 @@ void set_nonblock(int fd) int fl = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, fl | O_NONBLOCK); } + +void set_block(int fd) +{ + int fl = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, fl & ~O_NONBLOCK); +} + +int fork_and_flush_stdin(int fd, struct buffer *buffer) +{ + int i; + if (!buffer_len(buffer)) + return 0; + switch (fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + break; + default: + return 1; + } + for (i = 0; i < MAX_FDS; i++) + if (i != fd && i != 2) + close(i); + set_block(fd); + write_all(fd, buffer_data(buffer), buffer_len(buffer)); + exit(0); +} From 7f6a06c354339d01edee64f4aa711c88f88354e9 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Fri, 18 Mar 2011 11:16:05 +0100 Subject: [PATCH 68/86] qrexec: in write_stdin, remove dependency on write size Previous code could barf when write was partial; probably can happen only if we increase vchan buffer size, but it is better isolated now. --- qrexec/write_stdin.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/qrexec/write_stdin.c b/qrexec/write_stdin.c index 8ac1b221..f2bbae9e 100644 --- a/qrexec/write_stdin.c +++ b/qrexec/write_stdin.c @@ -58,24 +58,30 @@ int flush_client_data(int fd, int clid, struct buffer *buffer) } - int write_stdin(int fd, int clid, char *data, int len, struct buffer *buffer) { int ret; + int written = 0; if (buffer_len(buffer)) { buffer_append(buffer, data, len); return WRITE_STDIN_BUFFERED; } - - ret = write(fd, data, len); - if (ret == len) - return WRITE_STDIN_OK; - if (ret == -1) { - if (errno == EAGAIN) { + while (written < len) { + ret = write(fd, data + written, len - written); + if (ret == 0) { + perror("write_stdin: write returns 0 ???"); + exit(1); + } + if (ret == -1) { struct server_header s_hdr; - buffer_append(buffer, data, len); + + if (errno != EAGAIN) + return WRITE_STDIN_ERROR; + + buffer_append(buffer, data + written, + len - written); s_hdr.type = MSG_XOFF; s_hdr.clid = clid; @@ -83,13 +89,10 @@ int write_stdin(int fd, int clid, char *data, int len, write_all_vchan_ext(&s_hdr, sizeof s_hdr); return WRITE_STDIN_BUFFERED; - } else - return WRITE_STDIN_ERROR; - } else { - fprintf(stderr, - "writes < PIPE_BUF were supposed to be atomic ?\n"); - return WRITE_STDIN_ERROR; + } + written += ret; } + return WRITE_STDIN_OK; } From aa58bec1d9d5d1d5877ef7aa9f217b05ccd9d400 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Fri, 18 Mar 2011 14:12:19 +0100 Subject: [PATCH 69/86] Fixed default policy handling in firewall rules --- dom0/qvm-core/qubes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 176673dd..dd36bfd4 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1189,12 +1189,12 @@ class QubesProxyVm(QubesNetVm): reject_action = "REJECT --reject-with icmp-host-prohibited" if conf["allow"]: - rules_action = accept_action - default_action = reject_action + default_action = accept_action + rules_action = reject_action iptables += "-A FORWARD -i vif{0}.0 -p icmp -j ACCEPT\n".format(xid) else: - rules_action = reject_action - default_action = accept_action + default_action = reject_action + rules_action = accept_action for rule in conf["rules"]: iptables += "-A FORWARD -i vif{0}.0 -d {1}".format(xid, rule["address"]) From 55780b8c15c1326e2a292918e93aed70073640a0 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 18 Mar 2011 18:24:55 -0400 Subject: [PATCH 70/86] Indent fix --- common/block-snapshot | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/block-snapshot b/common/block-snapshot index a668935c..e787a018 100755 --- a/common/block-snapshot +++ b/common/block-snapshot @@ -176,10 +176,10 @@ case "$command" in done fi - if [ -e $node ]; then + if [ -e $node ]; then log debug "Removing $node" dmsetup remove $node - fi + fi # try to free loop devices for dev in $deps; do From 74d61e7f9a37a65198f0f0397ccec75158ebbccd Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 18 Mar 2011 18:54:14 -0400 Subject: [PATCH 71/86] Autocommit template changes after template shutdown (#96) --- common/block-snapshot | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/block-snapshot b/common/block-snapshot index e787a018..14752da2 100755 --- a/common/block-snapshot +++ b/common/block-snapshot @@ -174,6 +174,12 @@ case "$command" in dmsetup remove $snap fi done + # Commit template changes + domain=$(xenstore_read "$XENBUS_PATH/domain") + if [ "$domain" ]; then + # Dont stop on errors + /usr/bin/qvm-template-commit "$domain" || true + fi fi if [ -e $node ]; then From 823bd1ce0f7e903ac0f0243d1cf282e4e6559039 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 18 Mar 2011 22:15:32 -0400 Subject: [PATCH 72/86] Use common image for swap and root-cow - volatile.img (#118) This reduces xvd* devices count, so speeds up VM start. Also swap-cow is no longer needed, so remove this additional dm-snapshot layer. --- common/fstab | 2 +- dom0/qvm-core/qubes.py | 118 +++++++++++++-------- dom0/qvm-tools/qvm-prefs | 2 +- dom0/restore/qubes_prepare_saved_domain.sh | 2 +- dom0/restore/qubes_restore.c | 6 +- 5 files changed, 77 insertions(+), 53 deletions(-) diff --git a/common/fstab b/common/fstab index 968d9b77..45df7401 100644 --- a/common/fstab +++ b/common/fstab @@ -7,8 +7,8 @@ # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info # /dev/mapper/dmroot / ext4 defaults,noatime 1 1 -/dev/mapper/dmswap swap swap defaults 0 0 /dev/xvdb /rw ext4 noauto,defaults 0 0 +/dev/xvdc1 swap swap defaults 0 0 tmpfs /dev/shm tmpfs defaults 0 0 devpts /dev/pts devpts gid=5,mode=620 0 0 sysfs /sys sysfs defaults 0 0 diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 3999d0a2..ed769d08 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -60,7 +60,8 @@ vm_default_netmask = "255.255.0.0" default_root_img = "root.img" default_rootcow_img = "root-cow.img" -default_swapcow_img = "swap-cow.img" +default_volatile_img = "volatile.img" +default_clean_volatile_img = "clean-volatile.img" default_private_img = "private.img" default_appvms_conf_file = "appvm-template.conf" default_netvms_conf_file = "netvm-template.conf" @@ -82,11 +83,6 @@ dom0_vm = None qubes_appmenu_create_cmd = "/usr/lib/qubes/create_apps_for_appvm.sh" qubes_appmenu_remove_cmd = "/usr/lib/qubes/remove_appvm_appmenus.sh" -# TODO: we should detect the actual size of the AppVM's swap partition -# rather than using this ugly hardcoded value, which was choosen here -# as "should be good for everyone" -swap_cow_sz = 1024*1024*1024 - class XendSession(object): def __init__(self): self.get_xend_session_old_api() @@ -213,7 +209,7 @@ class QubesVm(object): self.root_img = dir_path + "/" + ( root_img if root_img is not None else default_root_img) - self.rootcow_img = dir_path + "/" + default_rootcow_img + self.volatile_img = dir_path + "/" + default_volatile_img if private_img is not None and os.path.isabs(private_img): self.private_img = private_img @@ -647,7 +643,7 @@ class QubesVm(object): attrs["dir_path"] = self.dir_path attrs["conf_file"] = self.conf_file attrs["root_img"] = self.root_img - attrs["rootcow_img"] = self.rootcow_img + attrs["volatile_img"] = self.volatile_img attrs["private_img"] = self.private_img attrs["uses_default_netvm"] = str(self.uses_default_netvm) attrs["netvm_qid"] = str(self.netvm_vm.qid) if self.netvm_vm is not None else "none" @@ -691,6 +687,13 @@ class QubesTemplateVm(QubesVm): super(QubesTemplateVm, self).__init__(**kwargs) dir_path = kwargs["dir_path"] + + # Clean image for root-cow and swap (AppVM side) + self.clean_volatile_img = self.dir_path + "/" + default_clean_volatile_img + + # Image for template changes + self.rootcow_img = self.dir_path + "/" + default_rootcow_img + if appvms_conf_file is not None and os.path.isabs(appvms_conf_file): self.appvms_conf_file = appvms_conf_file else: @@ -799,13 +802,21 @@ class QubesTemplateVm(QubesVm): raise IOError ("Error while copying {0} to {1}".\ format(src_template_vm.root_img, self.root_img)) if verbose: - print "--> Copying the template's root COW image:\n{0} ==>\n{1}".\ - format(src_template_vm.rootcow_img, self.rootcow_img) + print "--> Copying the template's clean volatile image:\n{0} ==>\n{1}".\ + format(src_template_vm.clean_volatile_img, self.clean_volatile_img) # We prefer to use Linux's cp, because it nicely handles sparse files - retcode = subprocess.call (["cp", src_template_vm.rootcow_img, self.rootcow_img]) + retcode = subprocess.call (["cp", src_template_vm.clean_volatile_img, self.clean_volatile_img]) if retcode != 0: raise IOError ("Error while copying {0} to {1}".\ - format(src_template_vm.root_img, self.root_img)) + format(src_template_vm.clean_volatile_img, self.clean_volatile_img)) + if verbose: + print "--> Copying the template's volatile image:\n{0} ==>\n{1}".\ + format(self.clean_volatile_img, self.volatile_img) + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", self.clean_volatile_img, self.volatile_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(self.clean_volatile_img, self.volatile_img)) if verbose: print "--> Copying the template's kernel dir:\n{0} ==>\n{1}".\ format(src_template_vm.kernels_dir, self.kernels_dir) @@ -816,6 +827,8 @@ class QubesTemplateVm(QubesVm): format(src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) shutil.copytree (src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) + # Create root-cow.img + self.commit_changes() def verify_files(self): if dry_run: @@ -847,6 +860,16 @@ class QubesTemplateVm(QubesVm): "VM private image file doesn't exist: {0}".\ format(self.private_img)) + if not os.path.exists (self.volatile_img): + raise QubesException ( + "VM volatile image file doesn't exist: {0}".\ + format(self.volatile_img)) + + if not os.path.exists (self.clean_volatile_img): + raise QubesException ( + "Clean VM volatile image file doesn't exist: {0}".\ + format(self.clean_volatile_img)) + if not os.path.exists (self.kernels_dir): raise QubesException ( "VM's kernels directory does not exist: {0}".\ @@ -858,6 +881,7 @@ class QubesTemplateVm(QubesVm): if dry_run: return + self.reset_volatile_storage() if not self.is_updateable(): raise QubesException ("Cannot start Template VM that is marked \"nonupdatable\"") @@ -866,6 +890,21 @@ class QubesTemplateVm(QubesVm): return super(QubesTemplateVm, self).start(debug_console=debug_console, verbose=verbose) + def reset_volatile_storage(): + assert not self.is_running(), "Attempt to clean volatile image of running Template VM!" + + print "--> Cleaning volatile image: {0}...".format (self.volatile_img) + if dry_run: + return + if os.path.exists (self.volatile_img): + os.remove (self.volatile_img) + + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", self.clean_volatile_img, self.volatile_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(self.clean_volatile_img, self.volatile_img)) + def commit_changes (self): assert not self.is_running(), "Attempt to commit changes on running Template VM!" @@ -890,6 +929,8 @@ class QubesTemplateVm(QubesVm): attrs["appvms_conf_file"] = self.appvms_conf_file attrs["netvms_conf_file"] = self.netvms_conf_file attrs["standalonevms_conf_file"] = self.standalonevms_conf_file + attrs["clean_volatile_img"] = self.clean_volatile_img + attrs["rootcow_img"] = self.rootcow_img return attrs class QubesCowVm(QubesVm): @@ -927,8 +968,6 @@ class QubesCowVm(QubesVm): self.template_vm = template_vm - self.swapcow_img = dir_path + "/" + default_swapcow_img - def set_updateable(self): if self.is_updateable(): return @@ -1001,6 +1040,9 @@ class QubesCowVm(QubesVm): raise IOError ("Error while copying {0} to {1}".\ format(template_root, self.root_img)) + # Create volatile.img + self.reset_volatile_storage() + def verify_files(self): if dry_run: return @@ -1034,45 +1076,35 @@ class QubesCowVm(QubesVm): raise QubesException("VM is already running!") if not self.is_updateable(): - self.reset_cow_storage() + self.reset_volatile_storage() - self.reset_swap_cow_storage() + self.reset_volatile_storage() return super(QubesCowVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) - def reset_cow_storage (self): + def reset_volatile_storage(): + assert not self.is_running(), "Attempt to clean volatile image of running VM!" - print "--> Resetting the COW storage: {0}...".format (self.rootcow_img) + # Only makes sense on template based VM + if not self.template_vm: + return + print "--> Cleaning volatile image: {0}...".format (self.volatile_img) if dry_run: return - # this is probbaly not needed, as open (..., "w") should remove the previous file - if os.path.exists (self.rootcow_img): - os.remove (self.rootcow_img) - - - f_cow = open (self.rootcow_img, "w") - f_root = open (self.template_vm.root_img, "r") - f_root.seek(0, os.SEEK_END) - f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img - f_cow.close () - f_root.close() - - def reset_swap_cow_storage (self): - print "--> Resetting the swap COW storage: {0}...".format (self.swapcow_img) - if os.path.exists (self.swapcow_img): - os.remove (self.swapcow_img) - - f_swap_cow = open (self.swapcow_img, "w") - f_swap_cow.truncate (swap_cow_sz) - f_swap_cow.close() + if os.path.exists (self.volatile_img): + os.remove (self.volatile_img) + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", self.template_vm.clean_volatile_img, self.volatile_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(self.template_vm.clean_volatile_img, self.volatile_img)) def remove_from_disk(self): if dry_run: return - subprocess.check_call ([qubes_appmenu_remove_cmd, self.name]) shutil.rmtree (self.dir_path) @@ -1536,14 +1568,6 @@ class QubesAppVm(QubesCowVm): return conf - def start(self, debug_console = False, verbose = False, preparing_dvm = False): - if dry_run: - return - - self.reset_swap_cow_storage() - - return super(QubesAppVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) - class QubesVmCollection(dict): """ A collection of Qubes VMs indexed by Qubes id (qid) diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index 743de112..8ee3abdf 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -46,7 +46,7 @@ def do_list(vm): print fmt.format ("root COW img", vm.rootcow_img) if vm.is_appvm(): print fmt.format ("root img", vm.template_vm.root_img) - print fmt.format ("root COW img", vm.rootcow_img) + print fmt.format ("root volatile img", vm.volatile_img) print fmt.format ("private img", vm.private_img) diff --git a/dom0/restore/qubes_prepare_saved_domain.sh b/dom0/restore/qubes_prepare_saved_domain.sh index 1d1bf0c3..a1bf0770 100755 --- a/dom0/restore/qubes_prepare_saved_domain.sh +++ b/dom0/restore/qubes_prepare_saved_domain.sh @@ -59,5 +59,5 @@ if ! xm save $1 $2 ; then fi rm -f $QMEMMAN_STOP cd $VMDIR -tar -Scvf saved_cows.tar root-cow.img swap-cow.img +tar -Scvf saved_cows.tar volatile.img echo "DVM savefile created successfully." diff --git a/dom0/restore/qubes_restore.c b/dom0/restore/qubes_restore.c index 3788fb66..3afd1472 100644 --- a/dom0/restore/qubes_restore.c +++ b/dom0/restore/qubes_restore.c @@ -239,10 +239,10 @@ char *build_dvm_ip(int netvm, int id) return buf; } -#define NAME_PATTERN "/root-cow.img" +#define NAME_PATTERN "/volatile.img" // replaces the unique portions of the savefile with per-dvm values // returns the name of VM the savefile was taken for -// by looking for /.../vmname/root-cow.img +// by looking for /.../vmname/volatile.img // normally, it should be "templatename-dvm" char *get_vmname_from_savefile(int fd) { @@ -258,7 +258,7 @@ char *get_vmname_from_savefile(int fd) name = strstr(buf + 20, NAME_PATTERN); if (!name) { fprintf(stderr, - "cannot find 'root-cow.img' in savefile\n"); + "cannot find 'volatile.img' in savefile\n"); exit(1); } *name = 0; From ee28ca10d48b04d98b63778bc52d4e6b6265c6c9 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 18 Mar 2011 22:18:31 -0400 Subject: [PATCH 73/86] Indent, blank lines --- dom0/qvm-core/qubes.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index ed769d08..1db1ad05 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -690,7 +690,7 @@ class QubesTemplateVm(QubesVm): # Clean image for root-cow and swap (AppVM side) self.clean_volatile_img = self.dir_path + "/" + default_clean_volatile_img - + # Image for template changes self.rootcow_img = self.dir_path + "/" + default_rootcow_img @@ -916,7 +916,6 @@ class QubesTemplateVm(QubesVm): if os.path.exists (self.rootcow_img): os.remove (self.rootcow_img) - f_cow = open (self.rootcow_img, "w") f_root = open (self.root_img, "r") f_root.seek(0, os.SEEK_END) From c461835ea78b695de35237ace843e9567abe6ebc Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 18 Mar 2011 22:19:03 -0400 Subject: [PATCH 74/86] Dont allow to change disable 'updateable' flag of standalone VM --- dom0/qvm-core/qubes.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 1db1ad05..d6821923 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -973,6 +973,12 @@ class QubesCowVm(QubesVm): raise QubesException ("Change 'updateable' flag is not supported. Please use qvm-create.") + def set_nonupdateable(self): + if self.is_updateable(): + return + + raise QubesException ("Change 'updateable' flag is not supported. Please use qvm-create.") + def create_config_file(self): conf_template = None if self.type == "NetVM": From a6ee9d66f5fd2a62ba84e698dca705ecf2764d22 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 18 Mar 2011 22:24:08 -0400 Subject: [PATCH 75/86] qvm-backup-{,restore} - support for standalone VMs Backup root.img instead of (non-existing) root-cow.img --- dom0/qvm-tools/qvm-backup | 4 ++-- dom0/qvm-tools/qvm-backup-restore | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dom0/qvm-tools/qvm-backup b/dom0/qvm-tools/qvm-backup index 923a5803..b2506460 100755 --- a/dom0/qvm-tools/qvm-backup +++ b/dom0/qvm-tools/qvm-backup @@ -124,8 +124,8 @@ def main(): #files_to_backup += file_to_backup(vm.dir_path + "/apps") if vm.is_updateable(): - sz = vm.get_disk_usage(vm.rootcow_img) - files_to_backup += file_to_backup(vm.rootcow_img, sz) + sz = vm.get_disk_usage(vm.root_img) + files_to_backup += file_to_backup(vm.root_img, sz) vm_sz += sz s = "" diff --git a/dom0/qvm-tools/qvm-backup-restore b/dom0/qvm-tools/qvm-backup-restore index 37fa128e..b64a8308 100755 --- a/dom0/qvm-tools/qvm-backup-restore +++ b/dom0/qvm-tools/qvm-backup-restore @@ -285,7 +285,7 @@ def main(): restore_vm_file (backup_dir, vm.conf_file) if vm.is_updateable(): - restore_vm_file (backup_dir, vm.rootcow_img) + restore_vm_file (backup_dir, vm.root_img) elif vm.is_template(): restore_vm_dir (backup_dir, vm.dir_path, qubes_templates_dir); From bc383b692d6086eb518b5592c4cc84e6f50173f8 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sat, 19 Mar 2011 17:05:00 -0400 Subject: [PATCH 76/86] Use clean-volatile.img.tar instead of unpacked one (#118) "tar x" is much faster than cp on sparse file --- dom0/qvm-core/qubes.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index d6821923..5fe706dc 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -61,7 +61,7 @@ vm_default_netmask = "255.255.0.0" default_root_img = "root.img" default_rootcow_img = "root-cow.img" default_volatile_img = "volatile.img" -default_clean_volatile_img = "clean-volatile.img" +default_clean_volatile_img = "clean-volatile.img.tar" default_private_img = "private.img" default_appvms_conf_file = "appvm-template.conf" default_netvms_conf_file = "netvm-template.conf" @@ -899,11 +899,10 @@ class QubesTemplateVm(QubesVm): if os.path.exists (self.volatile_img): os.remove (self.volatile_img) - # We prefer to use Linux's cp, because it nicely handles sparse files - retcode = subprocess.call (["cp", self.clean_volatile_img, self.volatile_img]) + retcode = subprocess.call (["tar", "xf", self.clean_volatile_img, "-C", self.dir_path]) if retcode != 0: - raise IOError ("Error while copying {0} to {1}".\ - format(self.clean_volatile_img, self.volatile_img)) + raise IOError ("Error while unpacking {0} to {1}".\ + format(self.template_vm.clean_volatile_img, self.volatile_img)) def commit_changes (self): @@ -1100,10 +1099,9 @@ class QubesCowVm(QubesVm): if os.path.exists (self.volatile_img): os.remove (self.volatile_img) - # We prefer to use Linux's cp, because it nicely handles sparse files - retcode = subprocess.call (["cp", self.template_vm.clean_volatile_img, self.volatile_img]) + retcode = subprocess.call (["tar", "xf", self.template_vm.clean_volatile_img, "-C", self.dir_path]) if retcode != 0: - raise IOError ("Error while copying {0} to {1}".\ + raise IOError ("Error while unpacking {0} to {1}".\ format(self.template_vm.clean_volatile_img, self.volatile_img)) def remove_from_disk(self): From a5a43cdbc77c7a9c017b998224e8ab70c55f9c26 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sat, 19 Mar 2011 17:05:53 -0400 Subject: [PATCH 77/86] Fix missing arg to reset_volatile_storage (#118) And do not call it twice... --- dom0/qvm-core/qubes.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 5fe706dc..0d9f88df 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -890,7 +890,7 @@ class QubesTemplateVm(QubesVm): return super(QubesTemplateVm, self).start(debug_console=debug_console, verbose=verbose) - def reset_volatile_storage(): + def reset_volatile_storage(self): assert not self.is_running(), "Attempt to clean volatile image of running Template VM!" print "--> Cleaning volatile image: {0}...".format (self.volatile_img) @@ -1079,14 +1079,11 @@ class QubesCowVm(QubesVm): if self.is_running(): raise QubesException("VM is already running!") - if not self.is_updateable(): - self.reset_volatile_storage() - self.reset_volatile_storage() return super(QubesCowVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) - def reset_volatile_storage(): + def reset_volatile_storage(self): assert not self.is_running(), "Attempt to clean volatile image of running VM!" # Only makes sense on template based VM From 481e9871c45924fbf95078fe39a2c5572b1cb7a8 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Mon, 21 Mar 2011 22:06:53 +0100 Subject: [PATCH 78/86] Implemented implicit rule to allow ICMP traffic in firewall --- dom0/qvm-core/qubes.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index dd36bfd4..e165e482 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1184,14 +1184,12 @@ class QubesProxyVm(QubesNetVm): iptables += "# '{0}' VM:\n".format(vm.name) iptables += "-A FORWARD ! -s {0}/32 -i vif{1}.0 -j DROP\n".format(vm.ip, xid) - accept_action = "ACCEPT" reject_action = "REJECT --reject-with icmp-host-prohibited" if conf["allow"]: default_action = accept_action rules_action = reject_action - iptables += "-A FORWARD -i vif{0}.0 -p icmp -j ACCEPT\n".format(xid) else: default_action = reject_action rules_action = accept_action @@ -1212,6 +1210,8 @@ class QubesProxyVm(QubesNetVm): # PREROUTING does DNAT to NetVM DNSes, so we need self.netvm_vm. properties iptables += "-A FORWARD -i vif{0}.0 -p udp -d {1} --dport 53 -j ACCEPT\n".format(xid,self.netvm_vm.gateway) iptables += "-A FORWARD -i vif{0}.0 -p udp -d {1} --dport 53 -j ACCEPT\n".format(xid,self.netvm_vm.secondary_dns) + if conf["allowIcmp"]: + iptables += "-A FORWARD -i vif{0}.0 -p icmp -j ACCEPT\n".format(xid) iptables += "-A FORWARD -i vif{0}.0 -j {1}\n".format(xid, default_action) @@ -1397,7 +1397,8 @@ class QubesAppVm(QubesCowVm): root = xml.etree.ElementTree.Element( "QubesFirwallRules", policy = "allow" if conf["allow"] else "deny", - dns = "allow" if conf["allowDns"] else "deny" + dns = "allow" if conf["allowDns"] else "deny", + icmp = "allow" if conf["allowIcmp"] else "deny" ) for rule in conf["rules"]: @@ -1431,7 +1432,7 @@ class QubesAppVm(QubesCowVm): return True def get_firewall_conf(self): - conf = { "rules": list(), "allow": True, "allowDns": True } + conf = { "rules": list(), "allow": True, "allowDns": True, "allowIcmp": True } try: tree = xml.etree.ElementTree.parse(self.firewall_conf) @@ -1439,6 +1440,7 @@ class QubesAppVm(QubesCowVm): conf["allow"] = (root.get("policy") == "allow") conf["allowDns"] = (root.get("dns") == "allow") + conf["allowIcmp"] = (root.get("icmp") == "allow") for element in root: rule = {} From 4e78284e4fc0ddb29bb075720a19937cb643653f Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 23 Mar 2011 09:31:44 +0100 Subject: [PATCH 79/86] block.qubes: pass arguments correctly to other scripts --- dom0/restore/block.qubes | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom0/restore/block.qubes b/dom0/restore/block.qubes index 435418a2..4aee7f31 100755 --- a/dom0/restore/block.qubes +++ b/dom0/restore/block.qubes @@ -28,7 +28,7 @@ hexnumber() process() { if ! [ "x""$1" = "xfile" ] ; then - exec /etc/xen/scripts/block "$@" + exec /etc/xen/scripts/block $ORIG_ARGS fi while true ; do dev=$(losetup -f --show $2) @@ -48,6 +48,8 @@ XENBUS_PATH="${XENBUS_PATH:?}" if ! [ "$1" = "add" ] || ! [ -f /var/run/qubes/fast_block_attach ] ; then exec /etc/xen/scripts/block "$@" fi + +ORIG_ARGS="$@" vars=$(xenstore-read "$XENBUS_PATH/type" "$XENBUS_PATH/params") process $vars From a814b522b9906cf69fde24fa1a9b540a387d8f06 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 23 Mar 2011 09:36:30 +0100 Subject: [PATCH 80/86] Fix permissions on the dvm template directory. Needed in case default_template-dvm VM was created in init scripts, and files are not writeble by group qubes. --- dom0/restore/qvm-create-default-dvm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dom0/restore/qvm-create-default-dvm b/dom0/restore/qvm-create-default-dvm index 0871227a..7692c6ee 100755 --- a/dom0/restore/qvm-create-default-dvm +++ b/dom0/restore/qvm-create-default-dvm @@ -28,8 +28,12 @@ if ! [ -d "/var/lib/qubes/vm-templates/$TEMPLATENAME" ] ; then exit 1 fi DVMTMPL="$TEMPLATENAME"-dvm -if ! [ -d "/var/lib/qubes/appvms/$DVMTMPL" ] ; then +DVMTMPLDIR="/var/lib/qubes/appvms/$DVMTMPL" +if ! [ -d "$DVMTMPLDIR" ] ; then if ! qvm-create -t "$TEMPLATENAME" -l gray "$DVMTMPL" ; then exit 1 ; fi + chgrp qubes "$DVMTMPLDIR" "$DVMTMPLDIR"/* + chmod 660 "$DVMTMPLDIR"/* + chmod 770 "$DVMTMPLDIR" fi if ! /usr/lib/qubes/qubes_prepare_saved_domain.sh \ "$DVMTMPL" "/var/lib/qubes/appvms/$DVMTMPL/dvm-savefile" $SCRIPTNAME ; then From 46190b9d829ad93198001c71cbd5867a6c5a6d37 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 23 Mar 2011 09:59:59 +0100 Subject: [PATCH 81/86] Copy kernel for standalone VM --- dom0/qvm-core/qubes.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 0d9f88df..442d50cd 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1044,6 +1044,13 @@ class QubesCowVm(QubesVm): raise IOError ("Error while copying {0} to {1}".\ format(template_root, self.root_img)) + kernels_dir = self.dir_path + '/' + default_kernels_subdir + if verbose: + print "--> Copying the template's kernel dir: {0}".\ + format(self.template_vm.kernels_dir) + shutil.copytree (self.template_vm.kernels_dir, kernels_dir) + + # Create volatile.img self.reset_volatile_storage() From dd9f1a6f7f34be161fd47320dc6c3741041f67a0 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 23 Mar 2011 11:34:01 +0100 Subject: [PATCH 82/86] Move execution of qrexec_agent to qubes_core Previously it was in both qubes_core_appvm and qubes_core_netvm; somehow counterintuitively, qubes_core_netvm executes on appvm, too. So move it to a common place. --- appvm/qubes_core_appvm | 1 - common/qubes_core | 2 ++ netvm/qubes_core_netvm | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appvm/qubes_core_appvm b/appvm/qubes_core_appvm index 25ff7f4c..ed250dd5 100755 --- a/appvm/qubes_core_appvm +++ b/appvm/qubes_core_appvm @@ -55,7 +55,6 @@ start() MEM_CHANGE_THRESHOLD_KB=30000 MEMINFO_DELAY_USEC=100000 /usr/lib/qubes/meminfo-writer $MEM_CHANGE_THRESHOLD_KB $MEMINFO_DELAY_USEC & - /usr/lib/qubes/qrexec_agent 2>/var/log/qubes/qrexec_agent.log & success echo "" diff --git a/common/qubes_core b/common/qubes_core index 26fe514d..c20f7d71 100755 --- a/common/qubes_core +++ b/common/qubes_core @@ -54,6 +54,8 @@ start() fi fi + /usr/lib/qubes/qrexec_agent 2>/var/log/qubes/qrexec_agent.log & + [ -x /rw/config/rc.local ] && /rw/config/rc.local success echo "" diff --git a/netvm/qubes_core_netvm b/netvm/qubes_core_netvm index 2268c018..c4d81dcb 100755 --- a/netvm/qubes_core_netvm +++ b/netvm/qubes_core_netvm @@ -32,7 +32,6 @@ start() /usr/lib/qubes/qubes_setup_dnat_to_ns echo "1" > /proc/sys/net/ipv4/ip_forward fi - /usr/lib/qubes/qrexec_agent 2>/var/log/qubes/qrexec_agent.log & success echo "" From 5350e5cc5bb1c21dbcb60a03185da42b2cf8896c Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 23 Mar 2011 11:46:53 +0100 Subject: [PATCH 83/86] move qrexec_agent out of core-netvm.spec It is already in core-appvm. --- rpm_spec/core-netvm.spec | 3 --- 1 file changed, 3 deletions(-) diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index c970acdb..222e496f 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -45,7 +45,6 @@ The Qubes core files for installation inside a Qubes NetVM. %pre %build -make -C ../qrexec make -C ../vchan make -C ../u2mfn @@ -56,7 +55,6 @@ mkdir -p $RPM_BUILD_ROOT/etc/init.d cp qubes_core_netvm $RPM_BUILD_ROOT/etc/init.d/ mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes -cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes cp ../common/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes cp ../common/qubes_fix_nm_conf.sh $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/etc/dhclient.d @@ -91,7 +89,6 @@ rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) /etc/init.d/qubes_core_netvm -/usr/lib/qubes/qrexec_agent /usr/lib/qubes/qubes_setup_dnat_to_ns /usr/lib/qubes/qubes_fix_nm_conf.sh /etc/dhclient.d/qubes_setup_dnat_to_ns.sh From 0b208e8664d91881be50384a233a8c06d734a603 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 23 Mar 2011 11:48:06 +0100 Subject: [PATCH 84/86] Move libs and /var/run/qubes out of qubes-netvm They are already in core-appvm package. --- rpm_spec/core-netvm.spec | 8 -------- 1 file changed, 8 deletions(-) diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index 222e496f..026ea0fb 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -65,10 +65,6 @@ cp ../netvm/30-qubes_external_ip $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d mkdir -p $RPM_BUILD_ROOT/var/run/qubes mkdir -p $RPM_BUILD_ROOT/etc/xen/scripts cp ../common/vif-route-qubes $RPM_BUILD_ROOT/etc/xen/scripts -install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so -install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so - -mkdir -p $RPM_BUILD_ROOT/var/run/qubes %post @@ -95,7 +91,3 @@ rm -rf $RPM_BUILD_ROOT /etc/NetworkManager/dispatcher.d/qubes_nmhook /etc/NetworkManager/dispatcher.d/30-qubes_external_ip /etc/xen/scripts/vif-route-qubes -%dir /var/run/qubes -%{_libdir}/libvchan.so -%{_libdir}/libu2mfn.so -%dir /var/run/qubes From a1f8cd907180c11713b1a066159c79019d6a6852 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 23 Mar 2011 13:26:39 +0100 Subject: [PATCH 85/86] When creating disposablevm object, pass non-None dirpath QubesVm constructor does not like it. --- dom0/qvm-core/qubes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 1a596021..8d5d3eeb 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1426,7 +1426,7 @@ class QubesDisposableVm(QubesVm): template_vm = kwargs.pop("template_vm") - super(QubesDisposableVm, self).__init__(dir_path=None, **kwargs) + super(QubesDisposableVm, self).__init__(dir_path="/nonexistent", **kwargs) qid = kwargs["qid"] assert template_vm is not None, "Missing template_vm for DisposableVM!" From f9b9b1ade6fc4bd60191c02d3c8e20ac34667b01 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Wed, 23 Mar 2011 13:40:28 +0100 Subject: [PATCH 86/86] qvm-create-default-dvm: fix permissions after creating savefile So, savefile.img and netvm_id.txt are correctly owned as well. --- dom0/restore/qvm-create-default-dvm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dom0/restore/qvm-create-default-dvm b/dom0/restore/qvm-create-default-dvm index 7692c6ee..f1f5c939 100755 --- a/dom0/restore/qvm-create-default-dvm +++ b/dom0/restore/qvm-create-default-dvm @@ -31,9 +31,6 @@ DVMTMPL="$TEMPLATENAME"-dvm DVMTMPLDIR="/var/lib/qubes/appvms/$DVMTMPL" if ! [ -d "$DVMTMPLDIR" ] ; then if ! qvm-create -t "$TEMPLATENAME" -l gray "$DVMTMPL" ; then exit 1 ; fi - chgrp qubes "$DVMTMPLDIR" "$DVMTMPLDIR"/* - chmod 660 "$DVMTMPLDIR"/* - chmod 770 "$DVMTMPLDIR" fi if ! /usr/lib/qubes/qubes_prepare_saved_domain.sh \ "$DVMTMPL" "/var/lib/qubes/appvms/$DVMTMPL/dvm-savefile" $SCRIPTNAME ; then @@ -57,3 +54,7 @@ else chmod 660 $SHMCOPY ln -s $SHMCOPY $CURRENT fi + +chgrp qubes "$DVMTMPLDIR" "$DVMTMPLDIR"/* +chmod 660 "$DVMTMPLDIR"/* +chmod 770 "$DVMTMPLDIR"