diff --git a/appvm/Makefile b/appvm/Makefile index ef88f081..6bb1dea6 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,9 +1,11 @@ CC=gcc CFLAGS=-Wall -all: qubes_penctl qubes_add_pendrive_script +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 + $(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 clean: - rm -f qubes_penctl qubes_add_pendrive_script *.o *~ + rm -f qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm *.o *~ diff --git a/appvm/dvm.h b/appvm/dvm.h new file mode 100644 index 00000000..b36d53fd --- /dev/null +++ b/appvm/dvm.h @@ -0,0 +1,6 @@ +#define DBDIR "/home/user/.dvm" +struct dvm_header { +unsigned long long file_size; +char name[1024-sizeof(unsigned long long)]; +}; + diff --git a/appvm/qubes_add_pendrive_script.c b/appvm/qubes_add_pendrive_script.c index 8c0fb917..1896e5f4 100644 --- a/appvm/qubes_add_pendrive_script.c +++ b/appvm/qubes_add_pendrive_script.c @@ -18,17 +18,25 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ - +#include +#include #include #include #include #include #include +#include +#include +#include +#include +#include "dvm.h" + int parse_events(char *buf, int len) { int i = 0; while (i < len) { - struct inotify_event *ev = (struct inotify_event *)(buf + i); + struct inotify_event *ev = + (struct inotify_event *) (buf + i); if ((ev->mask & IN_UNMOUNT) || (ev->mask & IN_IGNORED)) return 1; i += sizeof(struct inotify_event) + ev->len; @@ -69,19 +77,177 @@ void background() switch (fork()) { case -1: exit(1); - case 0: break; + case 0: + break; default: exit(0); } } +void check_legal_filename(char *name) +{ + if (index(name, '/')) { + syslog(LOG_DAEMON | LOG_ERR, + "the received filename contains /"); + exit(1); + } +} + +void drop_to_user() +{ + struct passwd *pw = getpwnam("user"); + if (pw) + setuid(pw->pw_uid); +} + +void copy_from_xvdh(int destfd, int srcfd, unsigned long long count) +{ + int n, size; + char buf[4096]; + unsigned long long total = 0; + while (total < count) { + if (count - total > sizeof(buf)) + size = sizeof(buf); + else + size = count - total; + n = read(srcfd, buf, size); + if (n != size) { + syslog(LOG_DAEMON | LOG_ERR, "reading xvdh"); + exit(1); + } + if (write(destfd, buf, size) != size) { + syslog(LOG_DAEMON | LOG_ERR, "writing file"); + exit(1); + } + total += size; + } +} + +void redirect_stderr() +{ + int fd = + open("/var/log/dvm.log", O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (fd < 0) { + syslog(LOG_DAEMON | LOG_ERR, "open dvm.log"); + exit(1); + } + dup2(fd, 2); +} + +void dvm_transaction_request(char *seq, struct xs_handle *xs) +{ + char filename[1024], cmdbuf[1024]; + struct dvm_header header; + int xvdh_fd, file_fd; + char *src_vm; + unsigned int len; + xvdh_fd = open("/dev/xvdh", O_RDONLY); + if (read(xvdh_fd, &header, sizeof(header)) != sizeof(header)) { + syslog(LOG_DAEMON | LOG_ERR, "read dvm_header"); + exit(1); + } + + header.name[sizeof(header.name) - 1] = 0; + check_legal_filename(header.name); + snprintf(filename, sizeof(filename), "/tmp/%s", header.name); + drop_to_user(); + + file_fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (file_fd < 0) { + syslog(LOG_DAEMON | LOG_ERR, "open file"); + exit(1); + } + copy_from_xvdh(file_fd, xvdh_fd, header.file_size); + close(xvdh_fd); + close(file_fd); + snprintf(cmdbuf, sizeof(cmdbuf), + "DISPLAY=:0 mimeopen -n '/tmp/%s'", header.name); + system(cmdbuf); + src_vm = xs_read(xs, XBT_NULL, "qubes_blocksrc", &len); + xs_write(xs, XBT_NULL, "device/qpen", "umount", 6); + xs_daemon_close(xs); + execl("/usr/bin/qvm-dvm-transfer", "qvm-dvm-transfer", src_vm, + filename, seq, NULL); + syslog(LOG_DAEMON | LOG_ERR, "execl qvm-dvm-transfer"); + exit(1); +} + +void dvm_transaction_return(char *seq_string, struct xs_handle *xs) +{ + int seq = strtoul(seq_string, 0, 10); + char db_name[1024]; + char file_name[1024]; + int db_fd, file_fd, xvdh_fd; + + struct dvm_header header; + xvdh_fd = open("/dev/xvdh", O_RDONLY); + if (xvdh_fd < 0) { + syslog(LOG_DAEMON | LOG_ERR, "open xvdh"); + exit(1); + } + if (read(xvdh_fd, &header, sizeof(header)) != sizeof(header)) { + syslog(LOG_DAEMON | LOG_ERR, "read dvm_header"); + exit(1); + } + drop_to_user(); + snprintf(db_name, sizeof(db_name), DBDIR "/%d", seq); + db_fd = open(db_name, O_RDONLY); + if (!db_fd) { + syslog(LOG_DAEMON | LOG_ERR, "open db"); + exit(1); + } + if (read(db_fd, file_name, sizeof(file_name)) < 0) { + syslog(LOG_DAEMON | LOG_ERR, "read db"); + exit(1); + } + close(db_fd); + file_fd = open(file_name, O_WRONLY | O_TRUNC); + if (file_fd < 0) { + syslog(LOG_DAEMON | LOG_ERR, "open filename"); + exit(1); + } + copy_from_xvdh(file_fd, xvdh_fd, header.file_size); + close(xvdh_fd); + close(file_fd); + xs_write(xs, XBT_NULL, "device/qpen", "umount", 6); + xs_daemon_close(xs); +} + + + + +void dvm_transaction(char *seq, struct xs_handle *xs) +{ + struct stat st; + redirect_stderr(); + if (stat("/etc/this_is_dvm", &st)) + dvm_transaction_return(seq, xs); + else + dvm_transaction_request(seq, xs); +} #define MOUNTDIR "/mnt/incoming" int main() { + struct xs_handle *xs; + char *seq; + unsigned int len; background(); + openlog("qubes_add_pendrive_script", LOG_CONS | LOG_PID, + LOG_DAEMON); + xs = xs_domain_open(); + if (!xs) { + syslog(LOG_DAEMON | LOG_ERR, "xs_domain_open"); + exit(1); + } + seq = xs_read(xs, XBT_NULL, "qubes_transaction_seq", &len); + if (seq && len > 0 && strcmp(seq, "0")) { + dvm_transaction(seq, xs); + exit(0); + } if (!system("su - user -c 'mount " MOUNTDIR "'")) wait_for_umount(MOUNTDIR "/."); - system("xenstore-write device/qpen umount"); + xs_write(xs, XBT_NULL, "device/qpen", "umount", 6); + xs_daemon_close(xs); return 0; } diff --git a/appvm/qvm-open-in-dvm.c b/appvm/qvm-open-in-dvm.c new file mode 100644 index 00000000..acfa81ce --- /dev/null +++ b/appvm/qvm-open-in-dvm.c @@ -0,0 +1,176 @@ +/* + * 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 _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include "dvm.h" + +void check_name(unsigned char *s) +{ + int c; + for (; *s; s++) { + c = *s; + if (c >= 'a' && c <= 'z') + continue; + if (c >= 'A' && c <= 'Z') + continue; + if (c == '_' || c == '-') + continue; + fprintf(stderr, "invalid string %s\n", s); + exit(1); + } +} + +int get_and_set_seq() +{ + int seq_fd, seq, n; + mkdir(DBDIR, 0700); + seq_fd = open(DBDIR "/seq", O_CREAT | O_RDWR, 0600); + if (seq_fd < 0) { + perror("open seq_fd"); + exit(1); + } + n = read(seq_fd, &seq, sizeof(seq)); + if (n < sizeof(seq)) + seq = 0; + seq++; + lseek(seq_fd, 0, SEEK_SET); + write(seq_fd, &seq, sizeof(seq)); + close(seq_fd); + return seq; +} + +void write_db(char *name, int seq) +{ + int db_fd; + char dbname[256]; + struct stat st; + if (!stat("/etc/this_is_dvm", &st)) + return; + snprintf(dbname, sizeof(dbname), DBDIR "/%d", seq); + db_fd = open(dbname, O_CREAT | O_WRONLY | O_TRUNC, 0600); + if (db_fd < 0) { + perror("open dbfile"); + exit(1); + } + if (write(db_fd, name, strlen(name) + 1) != strlen(name) + 1) { + perror("write db"); + exit(1); + } + close(db_fd); +} + +void copy_file(int xvdg_fd, int file_fd) +{ + int n; + char buf[4096]; + + for (;;) { + n = read(file_fd, buf, sizeof(buf)); + if (n < 0) { + perror("read file"); + exit(1); + } + if (n == 0) + break; + if (write(xvdg_fd, buf, n) != n) { + perror("write file"); + exit(1); + } + } +} + +int main(int argc, char **argv) +{ + struct dvm_header header = { 0, }; + struct stat st; + struct xs_handle *xs; + int seq; + int xvdg_fd, file_fd; + char *abs_filename; + char buf[4096]; + + if (argc != 3 && argc != 4) { + fprintf(stderr, "usage: %s vmname file\n", argv[0]); + exit(1); + } + check_name((unsigned char *) argv[1]); + if (argv[2][0] == '/') + abs_filename = argv[2]; + else { + char cwd[4096]; + getcwd(cwd, sizeof(cwd)); + asprintf(&abs_filename, "%s/%s", cwd, argv[2]); + } + if (stat(abs_filename, &st)) { + perror("stat file"); + exit(1); + } + header.file_size = st.st_size; + strncpy(header.name, rindex(abs_filename, '/') + 1, + sizeof(header.name) - 1); + xs = xs_domain_open(); + if (!xs) { + perror("xs_domain_open"); + exit(1); + } + if (!xs_write(xs, 0, "device/qpen", "new", 3)) { + perror("xs_write"); + exit(1); + } + while (stat("/dev/xvdg", &st)) + usleep(100000); + xvdg_fd = open("/dev/xvdg", O_WRONLY); + if (xvdg_fd < 0) { + perror("open xvdg"); + exit(1); + } + setuid(getuid()); + if (argc == 3) + seq = get_and_set_seq(); + else + seq = strtoul(argv[3], 0, 0); + file_fd = open(abs_filename, O_RDONLY); + if (file_fd < 0) { + perror("open file"); + exit(1); + } + if (write(xvdg_fd, &header, sizeof(header)) != sizeof(header)) { + perror("write filesize"); + exit(1); + } + copy_file(xvdg_fd, file_fd); + close(file_fd); + close(xvdg_fd); + snprintf(buf, sizeof(buf), "send %s %d", argv[1], seq); + if (!xs_write(xs, 0, "device/qpen", buf, strlen(buf))) { + perror("xs_write"); + exit(1); + } + write_db(abs_filename, seq); + xs_daemon_close(xs); + return 0; +} diff --git a/dom0/restore/qubes_restore.c b/dom0/restore/qubes_restore.c index 8ebb742f..f101f0f3 100644 --- a/dom0/restore/qubes_restore.c +++ b/dom0/restore/qubes_restore.c @@ -362,15 +362,28 @@ void write_varrun_domid(int domid) } +void redirect_stderr() +{ + int fd = + open("/var/run/qubes/qubes_restore.log", O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (fd < 0) { + syslog(LOG_DAEMON | LOG_ERR, "open dvm.log"); + exit(1); + } + dup2(fd, 2); +} + + int main(int argc, char **argv) { int fd, domid, dispid; char *resp; char *name; if (argc != 2 && argc != 3) { - fprintf(stderr, "usage: %s savefile\n", argv[0]); + fprintf(stderr, "usage: %s savefile [cmd] \n", argv[0]); exit(1); } + redirect_stderr(); fprintf(stderr, "time=%s, starting\n", gettime()); set_fast_flag(); atexit(rm_fast_flag); diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index 0319b3fe..c6cc1515 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -60,7 +60,10 @@ 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 qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/bin +cp qubes_timestamp qubes_add_pendrive_script qubes_penctl \ + qvm-copy-to-vm qvm-copy-to-vm.kde \ + qvm-open-in-dvm $RPM_BUILD_ROOT/usr/bin +ln -s qvm-open-in-dvm $RPM_BUILD_ROOT/usr/bin/qvm-dvm-transfer mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} cp qvm-copy.desktop $RPM_BUILD_ROOT/%{kde_service_dir} mkdir -p $RPM_BUILD_ROOT/etc/udev/rules.d @@ -178,6 +181,8 @@ rm -rf $RPM_BUILD_ROOT /etc/init.d/qubes_core /usr/bin/qvm-copy-to-vm /usr/bin/qvm-copy-to-vm.kde +%attr(4755,root,root) /usr/bin/qvm-open-in-dvm +/usr/bin/qvm-dvm-transfer %{kde_service_dir}/qvm-copy.desktop %attr(4755,root,root) /usr/bin/qubes_penctl /usr/bin/qubes_add_pendrive_script