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/fstab b/appvm/fstab index ac4eacbd..968d9b77 100644 --- a/appvm/fstab +++ b/appvm/fstab @@ -8,7 +8,7 @@ # /dev/mapper/dmroot / ext4 defaults,noatime 1 1 /dev/mapper/dmswap swap swap defaults 0 0 -/dev/xvdb /rw ext4 defaults 0 0 +/dev/xvdb /rw ext4 noauto,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/appvm/qubes.rules b/appvm/qubes.rules index e3fc50e4..5b3a502b 100644 --- a/appvm/qubes.rules +++ b/appvm/qubes.rules @@ -1 +1 @@ -SUBSYSTEM=="block", KERNEL=="xvdh", ACTION=="add", RUN+="/usr/bin/qubes_add_pendrive_script" +SUBSYSTEM=="block", KERNEL=="xvdh", ACTION=="add", RUN+="/usr/lib/qubes/qubes_add_pendrive_script" diff --git a/appvm/qubes_add_pendrive_script.c b/appvm/qubes_add_pendrive_script.c index 8c0fb917..bb708610 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,201 @@ void background() switch (fork()) { case -1: exit(1); - case 0: break; + case 0: + break; default: exit(0); } } +int check_legal_filename(char *name) +{ + if (index(name, '/')) { + syslog(LOG_DAEMON | LOG_ERR, + "the received filename contains /"); + return 0; + } + return 1; +} + +void drop_to_user() +{ + struct passwd *pw = getpwnam("user"); + if (pw) + setuid(pw->pw_uid); +} + +int 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"); + return 0; + } + if (write(destfd, buf, size) != size) { + syslog(LOG_DAEMON | LOG_ERR, "writing file"); + return 0; + } + total += size; + } + return 1; +} + +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 suicide(struct xs_handle *xs) +{ + xs_write(xs, XBT_NULL, "device/qpen", "killme", 6); + xs_daemon_close(xs); + exit(1); +} + +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; + struct stat stat_pre, stat_post; + xvdh_fd = open("/dev/xvdh", O_RDONLY); + if (read(xvdh_fd, &header, sizeof(header)) != sizeof(header)) { + syslog(LOG_DAEMON | LOG_ERR, "read dvm_header"); + suicide(xs); + } + + header.name[sizeof(header.name) - 1] = 0; + if (!check_legal_filename(header.name)) + suicide(xs); + 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"); + suicide(xs); + } + if (!copy_from_xvdh(file_fd, xvdh_fd, header.file_size)) + suicide(xs); + close(xvdh_fd); + close(file_fd); + if (stat(filename, &stat_pre)) { + syslog(LOG_DAEMON | LOG_ERR, "stat pre"); + suicide(xs); + } + snprintf(cmdbuf, sizeof(cmdbuf), + "DISPLAY=:0 mimeopen -n '/tmp/%s'", header.name); + if (system(cmdbuf)) + system("DISPLAY=:0 /usr/bin/kdialog --sorry 'Unable to handle mimetype of the requested file'"); + if (stat(filename, &stat_post)) { + syslog(LOG_DAEMON | LOG_ERR, "stat post"); + suicide(xs); + } + src_vm = xs_read(xs, XBT_NULL, "qubes_blocksrc", &len); + xs_write(xs, XBT_NULL, "device/qpen", "umount", 6); + if (stat_pre.st_mtime == stat_post.st_mtime) + suicide(xs); + xs_daemon_close(xs); + execl("/usr/lib/qubes/qvm-dvm-transfer", "qvm-dvm-transfer", src_vm, + filename, seq, NULL); + syslog(LOG_DAEMON | LOG_ERR, "execl qvm-dvm-transfer"); + suicide(xs); +} + +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"); + goto out_err; + } + if (read(xvdh_fd, &header, sizeof(header)) != sizeof(header)) { + syslog(LOG_DAEMON | LOG_ERR, "read dvm_header"); + goto out_err; + } + 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"); + goto out_err; + } + if (read(db_fd, file_name, sizeof(file_name)) < 0) { + syslog(LOG_DAEMON | LOG_ERR, "read db"); + goto out_err; + } + close(db_fd); + file_fd = open(file_name, O_WRONLY | O_TRUNC); + if (file_fd < 0) { + syslog(LOG_DAEMON | LOG_ERR, "open filename"); + goto out_err; + } + copy_from_xvdh(file_fd, xvdh_fd, header.file_size); + close(xvdh_fd); + close(file_fd); +out_err: + 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/qubes_core b/appvm/qubes_core index f432963c..2b6a1596 100755 --- a/appvm/qubes_core +++ b/appvm/qubes_core @@ -6,6 +6,18 @@ # Source function library. . /etc/rc.d/init.d/functions +possibly_run_save_script() +{ + ENCODED_SCRIPT=$(xenstore-read qubes_save_script) + if [ -z "$ENCODED_SCRIPT" ] ; then return ; fi + echo $ENCODED_SCRIPT|perl -e 'use MIME::Base64 qw(decode_base64); local($/) = undef;print decode_base64()' >/tmp/qubes_save_script + chmod 755 /tmp/qubes_save_script + Xorg -config /etc/X11/xorg-preload-apps.conf :0 & + sleep 2 + DISPLAY=:0 su - user -c /tmp/qubes_save_script + killall Xorg +} + start() { echo -n $"Executing Qubes Core scripts:" @@ -14,6 +26,22 @@ start() echo "ERROR: /usr/bin/xenstore-read not found!" exit 1 fi + if xenstore-read qubes_save_request 2>/dev/null ; then + possibly_run_save_script + touch /etc/this_is_dvm + dmesg -c >/dev/null +# echo 1 >/proc/sys/vm/drop_caches +# free | grep buffers/cache | +# (read a b c d ; xenstore-write device/qubes_used_mem $c) + free | grep Mem: | + (read a b c d ; xenstore-write device/qubes_used_mem $c) + echo "Waiting for restore" + while ! dmesg -c | grep "using vcpu" ; do usleep 10 ; done + while ! xenstore-read qubes_vm_type 2>/dev/null ; do + usleep 10 + done + echo Back to life. + fi name=$(/usr/bin/xenstore-read name) hostname $name @@ -29,30 +57,36 @@ start() echo "nameserver $secondary_dns" >> /etc/resolv.conf fi - if ! [ -d /rw/home ] ; then - echo - echo "--> Virgin boot of the VM: Linking /home to /rw/home" - mv /home /home.orig - mkdir -p /rw/config - mkdir -p /rw/home - ln -s /rw/home/ /home - cp -a /home.orig/user /home - touch /rw/config/rc.local - rm -fr /home.orig - touch /var/lib/qubes/first_boot_completed - fi + if [ -e /dev/xvdb ] ; then + mount /rw - if ! [ -L /home ] ; then - mv /home /home.orig - ln -s /rw/home /home - fi - if ! [ -L /usr/local ] ; then - mv /usr/local /usr/local.orig - ln -s /rw/usrlocal /usr/local - fi + if ! [ -d /rw/home ] ; then + echo + echo "--> Virgin boot of the VM: Linking /home to /rw/home" + mv /home /home.orig + mkdir -p /rw/config + mkdir -p /rw/home + ln -s /rw/home/ /home + cp -a /home.orig/user /home + touch /rw/config/rc.local + rm -fr /home.orig + touch /var/lib/qubes/first_boot_completed + fi + + if ! [ -L /home ] ; then + mv /home /home.orig + ln -s /rw/home /home + fi + if ! [ -L /usr/local ] ; then + mv /usr/local /usr/local.orig + ln -s /rw/usrlocal /usr/local + fi #make it last, we want all above to work without /rw mounted - if ! [ -d /rw/usrlocal ] ; then - cp -a /usr/local.orig /rw/usrlocal + if ! [ -d /rw/usrlocal ] ; then + cp -a /usr/local.orig /rw/usrlocal + fi + else + ln -sf /home_volatile /home fi [ -x /rw/config/rc.local ] && /rw/config/rc.local diff --git a/appvm/qubes_penctl.c b/appvm/qubes_penctl.c index ab373f02..878652e3 100644 --- a/appvm/qubes_penctl.c +++ b/appvm/qubes_penctl.c @@ -24,7 +24,7 @@ #include #include #include -int check_name(unsigned char *s) +void check_name(unsigned char *s) { int c; for (; *s; s++) { @@ -35,14 +35,21 @@ int check_name(unsigned char *s) continue; if (c == '_' || c == '-') continue; - return 0; + fprintf(stderr, "invalid string %s\n", s); + exit(1); } - return 1; +} + +void usage(char *argv0) +{ + fprintf(stderr, "usage: %s [new|umount]\n" + "%s send vmname [seq]\n", argv0, argv0); + exit(1); } int main(int argc, char **argv) { - char buf[256] = "new"; + char buf[256]; struct xs_handle *xs; xs = xs_domain_open(); setuid(getuid()); @@ -50,18 +57,25 @@ int main(int argc, char **argv) perror("xs_domain_open"); exit(1); } - if (argc < 2) { - fprintf(stderr, "usage: %s new\n" - "%s send vmname\n", argv[0], argv[0]); - exit(1); - } - if (argc > 2) { - if (!check_name((unsigned char*)argv[2])) { - fprintf(stderr, "invalid vmname %s\n", argv[2]); - exit(1); - } + switch (argc) { + case 2: + if (!strcmp(argv[1], "umount")) + strcpy(buf, "umount"); + else + strcpy(buf, "new"); + break; + case 3: + check_name((unsigned char *) argv[2]); snprintf(buf, sizeof(buf), "send %s", argv[2]); + break; + case 4: + check_name((unsigned char *) argv[2]); + check_name((unsigned char *) argv[3]); + snprintf(buf, sizeof(buf), "send %s %s", argv[2], argv[3]); + default: + usage(argv[0]); } + if (!xs_write(xs, 0, "device/qpen", buf, strlen(buf))) { perror("xs_write"); exit(1); diff --git a/appvm/qubes_timestamp b/appvm/qubes_timestamp new file mode 100755 index 00000000..fc3d8d75 --- /dev/null +++ b/appvm/qubes_timestamp @@ -0,0 +1,2 @@ +#!/bin/sh +exec xenstore-write device/qubes_timestamp $(date +%s.%N) diff --git a/appvm/qvm-copy-to-vm b/appvm/qvm-copy-to-vm index def32d7c..1af03ec0 100755 --- a/appvm/qvm-copy-to-vm +++ b/appvm/qvm-copy-to-vm @@ -24,7 +24,7 @@ if [ $# -lt 2 ] ; then echo usage: $0 'vmname file [file]*' exit 1 fi -qubes_penctl new || exit 1 +/usr/lib/qubes/qubes_penctl new || exit 1 echo -n Waiting for the Qubes virtual pendrive while ! [ -e /dev/xvdg ] ; do echo -n . @@ -37,4 +37,4 @@ shift cp -v -a "$@" /mnt/outgoing #sometimes Dolphin lags a bit umount /mnt/outgoing || (sleep 1; umount /mnt/outgoing) || exit 1 -qubes_penctl send $VMNAME || exit 1 +/usr/lib/qubes/qubes_penctl send $VMNAME || exit 1 diff --git a/appvm/qvm-copy.desktop b/appvm/qvm-copy.desktop index 6d7ebc66..5795eb61 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/bin/qvm-copy-to-vm.kde %U +Exec=/usr/lib/qubes/qvm-copy-to-vm.kde %U Icon=kget Name=Send To VM diff --git a/appvm/qvm-dvm.desktop b/appvm/qvm-dvm.desktop new file mode 100644 index 00000000..a7f5ad77 --- /dev/null +++ b/appvm/qvm-dvm.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Actions=QvmDvm; +Type=Service +X-KDE-ServiceTypes=KonqPopupMenu/Plugin,all/allfiles + +[Desktop Action QvmDvm] +Exec=/usr/bin/qvm-open-in-dvm disposable %U +Icon=kget +Name=Open In DisposableVM + 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/appvm/xorg-preload-apps.conf b/appvm/xorg-preload-apps.conf new file mode 100644 index 00000000..afa479bc --- /dev/null +++ b/appvm/xorg-preload-apps.conf @@ -0,0 +1,30 @@ +Section "ServerLayout" + Identifier "Default Layout" + Screen 0 "Screen0" 0 0 +EndSection + +Section "Device" + Identifier "Videocard0" + Driver "dummy" + VideoRam 4001 +EndSection + +Section "Monitor" + Identifier "Monitor0" + HorizSync 49-50 + VertRefresh 62-63 + Modeline "QB1280x800" 64 1280 1281 1282 1283 800 801 802 803 +EndSection + +Section "Screen" + Identifier "Screen0" + Device "Videocard0" + Monitor "Monitor0" + DefaultDepth 24 + SubSection "Display" + Viewport 0 0 + Depth 24 + Modes "QB1280x800" + EndSubSection +EndSection + diff --git a/common/qubes_nmhook b/common/qubes_nmhook index d06cd57d..b9028de8 100755 --- a/common/qubes_nmhook +++ b/common/qubes_nmhook @@ -1,2 +1,2 @@ #!/bin/sh -/usr/bin/qubes_setup_dnat_to_ns +/usr/lib/qubes/qubes_setup_dnat_to_ns diff --git a/common/qubes_setup_dnat_to_ns b/common/qubes_setup_dnat_to_ns index e4841919..67a1b90b 100755 --- a/common/qubes_setup_dnat_to_ns +++ b/common/qubes_setup_dnat_to_ns @@ -10,7 +10,7 @@ addrule() fi } export PATH=$PATH:/sbin:/bin -source /var/run/qubes_ns +source /var/run/qubes/qubes_ns if [ "X"$NS1 = "X" ] ; then exit ; fi iptables -t nat -F PREROUTING FIRSTONE=yes diff --git a/dom0/init.d/qubes_core b/dom0/init.d/qubes_core index e0945572..8dc9a10c 100755 --- a/dom0/init.d/qubes_core +++ b/dom0/init.d/qubes_core @@ -16,7 +16,27 @@ # Source function library. . /etc/rc.d/init.d/functions - +setup_dvm_files() +{ + ROOT=/var/lib/qubes/dvmdata/savefile_root + DEFAULT=/var/lib/qubes/dvmdata/default_savefile + if ! [ -f $DEFAULT ] ; then return ; fi + if ! [ -f $ROOT ] ; then return ; fi + if [ $ROOT -nt $DEFAULT ] ; then + echo DVM require reconfiguration + return + fi + if [ -f /var/lib/qubes/dvmdata/dont_use_shm ] ; then + ln -s $DEFAULT /var/run/qubes/current_savefile + else + mkdir -m 770 /dev/shm/qubes + chown root.qubes /dev/shm/qubes + cp $DEFAULT /dev/shm/qubes/current_savefile + chown root.qubes /dev/shm/qubes/current_savefile + chmod 660 /dev/shm/qubes/current_savefile + ln -s /dev/shm/qubes/current_savefile /var/run/qubes/current_savefile + fi +} start() { @@ -30,7 +50,12 @@ start() chgrp qubes /var/run/xenstored/* chmod 660 /var/run/xenstored/* xm sched-credit -d 0 -w 65535 + printf "\x00\x00\x00\x00" > /var/run/qubes/dispVM_seq + chown root:qubes /var/run/qubes/dispVM_seq + chmod 660 /var/run/qubes/dispVM_seq + xm mem-set 0 800 cp /var/lib/qubes/qubes.xml /var/lib/qubes/backup/qubes-$(date +%F-%T).xml + setup_dvm_files touch /var/lock/subsys/qubes_core success echo diff --git a/dom0/init.d/qubes_netvm b/dom0/init.d/qubes_netvm index 3ecf7554..5e66b695 100755 --- a/dom0/init.d/qubes_netvm +++ b/dom0/init.d/qubes_netvm @@ -31,11 +31,13 @@ start() echo -n $"Setting up net backend in Dom0:" brctl addbr br0 || exit 1 + brctl stp br0 off + brctl setfd br0 1 ifconfig br0 10.0.0.1 netmask 255.255.0.0 up || exit 1 ifconfig br0:1 10.0.255.254 netmask 255.255.0.0 up || exit 1 - echo "NS1=10.0.0.1" > /var/run/qubes_ns - echo "NS2=10.0.255.254" >> /var/run/qubes_ns - qubes_setup_dnat_to_ns + echo "NS1=10.0.0.1" > /var/run/qubes/qubes_ns + echo "NS2=10.0.255.254" >> /var/run/qubes/qubes_ns + /usr/lib/qubes/qubes_setup_dnat_to_ns echo "1" > /proc/sys/net/ipv4/ip_forward || exit 1 else diff --git a/dom0/pendrive_swapper/qfilexchgd b/dom0/pendrive_swapper/qfilexchgd index f74e6951..be7f4650 100755 --- a/dom0/pendrive_swapper/qfilexchgd +++ b/dom0/pendrive_swapper/qfilexchgd @@ -22,6 +22,7 @@ import xen.lowlevel.xs import os +import os.path import sys import subprocess import daemon @@ -32,6 +33,8 @@ from qubes.qubes import QubesDaemonPidfile filename_seq = 50 pen_cmd = '/usr/lib/qubes/qubes_pencmd' +disposable_domains_dict = {} +current_savefile = '/var/run/qubes/current_savefile' def get_next_filename_seq(): global filename_seq @@ -71,7 +74,13 @@ class DomainState: self.send_seq = None self.rcv_seq = None self.waiting_sender = None + self.allowed_dest = None + self.allowed_seq = None + def killme(self): + if not os.path.isfile('/etc/debug-dvm'): + subprocess.call(['/usr/sbin/xm', 'destroy', self.domain_id]) + def handle_request(self, request): req_ok = False if request is None: @@ -79,9 +88,16 @@ class DomainState: tmp = request.split() rq = tmp[0] if len(tmp) > 1: - arg = tmp[1] + vmname = tmp[1] else: - arg = None + vmname = None + if len(tmp) > 2: + transaction_seq = tmp[2] + else: + transaction_seq = '0' + if rq == 'killme': + self.killme() + req_ok = True if rq == 'new' and self.send_state == 'idle': self.send_seq = get_next_filename_seq() retcode = subprocess.call([pen_cmd, 'new', self.domain_id, self.send_seq]) @@ -89,9 +105,9 @@ class DomainState: if retcode == 0: self.send_state = 'has_clean_pendrive' req_ok = True - if rq == 'send' and self.send_state == 'has_clean_pendrive' and arg is not None: - logproc( 'send from ' + self.domain_id + ' to ' + arg) - if self.handle_transfer(arg): + if rq == 'send' and self.send_state == 'has_clean_pendrive' and vmname is not None: + logproc( 'send from ' + self.domain_id + ' to ' + vmname) + if self.handle_transfer(vmname, transaction_seq): self.send_state = 'idle' req_ok = True; if rq == 'umount' and self.rcv_state == 'has_loaded_pendrive': @@ -112,7 +128,7 @@ class DomainState: tmp = self.waiting_sender self.waiting_sender = None if tmp.send_state == 'has_clean_pendrive': - if tmp.handle_transfer(self.name): + if tmp.handle_transfer(self.name, self.delayed_transaction_seq): tmp.send_state = 'idle' if not req_ok: @@ -129,7 +145,7 @@ class DomainState: else: return False - def handle_transfer(self, vmname): + def handle_transfer_regular(self, vmname, transaction_seq): qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_reading() qvm_collection.load() @@ -147,19 +163,89 @@ class DomainState: if self.ask_to_umount(vmname): target.rcv_state='waits_to_umount' target.waiting_sender=self + self.delayed_transaction_seq=transaction_seq logproc( 'target domain ' + target.domain_id + ' is not idle, now ' + target.rcv_state) return False - retcode = subprocess.call(['/usr/bin/kdialog', '--yesno', 'Do you authorize pendrive transfer from ' + self.name + ' to ' + vmname + '?' , '--title', 'Security confirmation']) - logproc('handle_transfer: kdialog retcode=' + str(retcode)) - if retcode != 0: - return False + if self.allowed_seq is not None: + if self.allowed_seq != transaction_seq or self.allowed_dest != target.name: + logproc('sender ' + self.name + ' receiver ' + target.name + ' : allowed attributes mismatch, denied') + return False + else: + transaction_seq = '0' + retcode = subprocess.call(['/usr/bin/kdialog', '--yesno', 'Do you authorize pendrive transfer from ' + self.name + ' to ' + vmname + '?' , '--title', 'Security confirmation']) + logproc('handle_transfer: kdialog retcode=' + str(retcode)) + if retcode != 0: + return False target.rcv_state='has_loaded_pendrive' - retcode = subprocess.call([pen_cmd, 'send', self.domain_id, target.domain_id, self.send_seq]) + retcode = subprocess.call([pen_cmd, 'send', self.domain_id, target.domain_id, self.send_seq, transaction_seq]) target.rcv_seq = self.send_seq self.send_seq = None logproc( 'set state of ' + target.domain_id + ' to has_loaded_pendrive, retcode=' + str(retcode)) + if self.allowed_seq is not None: + self.killme() return True + def handle_transfer_disposable(self, transaction_seq): + + 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: + logproc( 'Domain ' + vmname + ' does not exist ?') + qvm_collection.unlock_db() + return False + retcode = subprocess.call(['/usr/lib/qubes/qubes_restore', + current_savefile, + '-c', vm.label.color, + '-i', vm.label.icon, + '-l', str(vm.label.index)]) + if retcode != 0: + subprocess.call(['/usr/bin/kdialog', '--sorry', 'DisposableVM creation failed, see qubes_restore.log']) + qvm_collection.unlock_db() + return False + 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: + logproc( 'Domain ' + disptempl + ' does not exist ?') + qvm_collection.unlock_db() + return False + qvm_collection.add_new_appvm(disp_name, vm_disptempl.template_vm, label=vm.label) + qvm_collection.save() + qvm_collection.unlock_db() + + dispdom = DomainState(disp_xid, self.domdict) + disposable_domains_dict[disp_xid] = dispdom + retcode = subprocess.call([pen_cmd, 'send', self.domain_id, disp_xid, self.send_seq, transaction_seq]) + dispdom.rcv_seq = self.send_seq + dispdom.rcv_state = 'has_loaded_pendrive' + dispdom.allowed_dest = self.name + dispdom.allowed_seq = transaction_seq + self.send_seq = None + logproc( 'sent pendrive to disposable xid ' + disp_xid) + return True + + def handle_transfer(self, vmname, transaction_seq): + dvmdata_dir = '/var/lib/qubes/dvmdata/' + if vmname != 'disposable': + return self.handle_transfer_regular(vmname, transaction_seq) + if not os.path.isfile(current_savefile): + subprocess.call(['/usr/bin/kdialog', '--sorry', 'There is no current savefile defined; run Qubes Manager']) + return False + if not os.path.isfile(dvmdata_dir+'default_savefile') or not os.path.isfile(dvmdata_dir+'savefile_root'): + subprocess.call(['/usr/bin/kdialog', '--sorry', 'Default savefile misconfiguration; run Qubes Manager']) + 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: + subprocess.call(['/usr/bin/kdialog', '--sorry', 'Your current savefile is outdated, as you have updated the template VM. Run Qubes Manager and recreate the savefile']) + return False + return self.handle_transfer_disposable(transaction_seq) class XS_Watcher: def __init__(self): @@ -172,15 +258,21 @@ class XS_Watcher: if curr == None: return for i in only_in_first_list(curr, self.domdict.keys()): - newdom = DomainState(i, self.domdict) + if disposable_domains_dict.has_key(i): + newdom = disposable_domains_dict[i] + else: + newdom = DomainState(i, self.domdict) + newdom.name = '' + self.domdict[i] = newdom newdom.watch_token = WatchType(XS_Watcher.request, newdom) newdom.watch_name = WatchType(XS_Watcher.namechange, newdom) - self.domdict[i] = newdom self.handle.watch(get_req_node(i), newdom.watch_token) self.handle.watch(get_name_node(i), newdom.watch_name) - newdom.name = '' logproc( 'added domain ' + i) for i in only_in_first_list(self.domdict.keys(), curr): + if disposable_domains_dict.has_key(i): + self.remove_disposable_from_qdb(self.domdict[i].name) + disposable_domains_dict.pop(i) self.handle.unwatch(get_req_node(i), self.domdict[i].watch_token) self.handle.unwatch(get_name_node(i), self.domdict[i].watch_name) self.domdict.pop(i) @@ -196,6 +288,20 @@ class XS_Watcher: domain_param.name = ret logproc( 'Name for domain xid ' + domain_param.domain_id + ' is ' + ret ) + def remove_disposable_from_qdb(self, name): + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_writing() + qvm_collection.load() + + vm = qvm_collection.get_vm_by_name(name) + if vm is None: + logproc( 'remove_disposable_from_qdb: Domain ' + vmname + ' does not exist ?') + qvm_collection.unlock_db() + return False + qvm_collection.pop(vm.qid) + qvm_collection.save() + qvm_collection.unlock_db() + def watch_loop(self): sys.stderr = file('/var/log/qubes/qfileexchgd.errors', 'a') while True: diff --git a/dom0/pendrive_swapper/qubes_pencmd b/dom0/pendrive_swapper/qubes_pencmd index 9f1db8fb..3982b25b 100755 --- a/dom0/pendrive_swapper/qubes_pencmd +++ b/dom0/pendrive_swapper/qubes_pencmd @@ -41,10 +41,11 @@ function do_send() FILE=$SHARE_DIR/pendrive.$3 vmname=$(xenstore-read /local/domain/$1/name) xenstore-write /local/domain/$2/qubes_blocksrc $vmname + xenstore-write /local/domain/$2/qubes_transaction_seq "$4" xm block-detach $1 /dev/xvdg || exit 1 xm block-attach $2 file:/$FILE /dev/xvdh w || exit 1 } - +export PATH=$PATH:/sbin:/usr/sbin if [ $# -lt 1 ] ; then echo args missing ? exit 1 @@ -62,11 +63,11 @@ elif [ "$1" = "umount" ] ; then fi do_umount "$2" "$3" elif [ "$1" = "send" ] ; then - if ! [ $# = 4 ] ; then - echo send requires 3 more args + if ! [ $# = 5 ] ; then + echo send requires 4 more args exit 1 fi - do_send "$2" "$3" "$4" + do_send "$2" "$3" "$4" "$5" else echo bad cmd exit 1 diff --git a/dom0/restore/Makefile b/dom0/restore/Makefile new file mode 100644 index 00000000..e279bbb6 --- /dev/null +++ b/dom0/restore/Makefile @@ -0,0 +1,9 @@ +CC=gcc +all: qubes_restore xenstore-watch +qubes_restore: qubes_restore.o + $(CC) -o qubes_restore qubes_restore.o -lxenstore + +xenstore-watch: xenstore-watch.o + $(CC) -o xenstore-watch xenstore-watch.o -lxenstore +clean: + rm -f *.o *~ qubes_restore xenstore-watch diff --git a/dom0/restore/block.qubes b/dom0/restore/block.qubes new file mode 100755 index 00000000..435418a2 --- /dev/null +++ b/dom0/restore/block.qubes @@ -0,0 +1,54 @@ +#!/bin/bash + +hd_arr[10]=a +hd_arr[11]=b +hd_arr[12]=c +hd_arr[13]=d +hd_arr[14]=e +hd_arr[15]=f + +hexdigit() +{ + if [ $1 -lt 10 ] ; then + RET=$1 + else + RET=${hd_arr[$1]} + fi +} + +hexnumber() +{ + hexdigit $(($1/16)) + ret2=$RET + hexdigit $(($1%16)) + HEXNUMBER="$ret2"$RET +} + + +process() +{ + if ! [ "x""$1" = "xfile" ] ; then + exec /etc/xen/scripts/block "$@" + fi + while true ; do + dev=$(losetup -f --show $2) + if [ -n "$dev" ] ; then break ; fi + done + hexnumber ${dev:9:70} + xenstore-write "$XENBUS_PATH/node" "$dev" \ + "$XENBUS_PATH/physical-device" "7:"$HEXNUMBER \ + "$XENBUS_PATH/hotplug-status" connected +} + +#exec 2>>/tmp/block.$$ +#set -x +export PATH="/sbin:/bin:/usr/bin:/usr/sbin:$PATH" + +XENBUS_PATH="${XENBUS_PATH:?}" +if ! [ "$1" = "add" ] || ! [ -f /var/run/qubes/fast_block_attach ] ; then + exec /etc/xen/scripts/block "$@" +fi + +vars=$(xenstore-read "$XENBUS_PATH/type" "$XENBUS_PATH/params") +process $vars +exit 0 diff --git a/dom0/restore/qubes_prepare_saved_domain.sh b/dom0/restore/qubes_prepare_saved_domain.sh new file mode 100755 index 00000000..7c9ca5c4 --- /dev/null +++ b/dom0/restore/qubes_prepare_saved_domain.sh @@ -0,0 +1,58 @@ +#!/bin/sh +get_encoded_script() +{ + if ! [ -f "$1" ] ; then + echo $1 is not a file ? + exit 1 + fi + ENCODED_SCRIPT=`cat "$1" | perl -e 'use MIME::Base64 qw(encode_base64); local($/) = undef;print encode_base64()'|tr -d "\n"` +} + +if [ $# != 2 -a $# != 3 ] ; then + echo usage: $0 domainname savefile_to_be_created [preload script] + exit 1 +fi +export PATH=$PATH:/sbin:/usr/sbin +if [ $# = 3 ] ; then + get_encoded_script $3 +fi +VMDIR=/var/lib/qubes/appvms/$1 +if ! [ -d $VMDIR ] ; then + echo $VMDIR does not exist ? + exit 1 +fi +if ! qvm-start $1 --no-guid ; then + exit 1 +fi + +ID=none +for i in $(xenstore-list /local/domain) ; do + name=$(xenstore-read /local/domain/$i/name) + if [ "x"$name = "x"$1 ] ; then + ID=$i + fi +done +if [ $ID = none ] ; then + echo cannot get domain id + exit 1 +fi +echo domainid=$ID +if [ -n "$ENCODED_SCRIPT" ] ; then + xenstore-write /local/domain/$ID/qubes_save_script "$ENCODED_SCRIPT" +fi +set -x +xenstore-write /local/domain/$ID/qubes_save_request 1 +xenstore-watch /local/domain/$ID/device/qubes_used_mem +xenstore-read /local/domain/$ID/qubes_gateway | \ + cut -d . -f 2 | tr -d "\n" > $VMDIR/netvm_id.txt +xm block-detach $1 /dev/xvdb +MEM=$(xenstore-read /local/domain/$ID/device/qubes_used_mem) +echo MEM=$MEM +xm mem-set $1 $(($MEM/1000)) +sleep 1 +touch $2 +if ! xm save $1 $2 ; then exit 1 ; fi +cd $VMDIR +tar -Scvf saved_cows.tar root-cow.img swap-cow.img + + diff --git a/dom0/restore/qubes_restore.c b/dom0/restore/qubes_restore.c new file mode 100644 index 00000000..9ab3550e --- /dev/null +++ b/dom0/restore/qubes_restore.c @@ -0,0 +1,427 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char xmlrpc_header[] = + "POST /RPC2 HTTP/1.0\r\n" + "Host: \r\n" + "User-Agent: xmlrpclib.py/1.0.1 (by www.pythonware.com)\r\n" + "Content-Type: text/xml\r\n" "Content-Length: %d\r\n" "\r\n"; +char xmlrpc_body_restore[] = + "\n" + "\n" + "xend.domain.restore\n" + "\n" + "\n" + "%s\n" + "\n" + "\n" + "0\n" + "\n" "\n" "\n"; + +char xmlrpc_body_setmem[] = + "\n\nxend.domain.setMemoryTarget\n\n\n%d\n\n\n%d\n\n\n\n"; + +void send_raw(int fd, char *body) +{ + char *header; + asprintf(&header, xmlrpc_header, strlen(body)); + if (write(fd, header, strlen(header)) != strlen(header)) { + perror("write xend"); + exit(1); + } + if (write(fd, body, strlen(body)) != strlen(body)) { + perror("write xend"); + exit(1); + } + shutdown(fd, SHUT_WR); +} + + +void send_req_restore(int fd, char *name) +{ + char *body; + asprintf(&body, xmlrpc_body_restore, name); + send_raw(fd, body); +} + +void send_req_setmem(int fd, int domid, int mem) +{ + char *body; + asprintf(&body, xmlrpc_body_setmem, domid, mem); + send_raw(fd, body); +} + +char *recv_resp(int fd) +{ +#define RESPSIZE 65536 + static char buf[RESPSIZE]; + int total = 0; + int n; + for (;;) { + n = read(fd, buf + total, RESPSIZE - total); + if (n == 0) { + buf[total] = 0; + close(fd); + return buf; + } + if (n < 0) { + perror("xend read"); + exit(1); + } + total += n; + } +} + +void bad_resp(char *resp) +{ + fprintf(stderr, "Error; Xend response:\n%s\n", resp); + exit(1); +} + +int parse_resp(char *resp) +{ + char *domid; + if (strstr(resp, "")) + bad_resp(resp); + if (!strstr(resp, "domid")) + bad_resp(resp); + domid = strstr(resp, ""); + if (!domid) + bad_resp(resp); + return strtoul(domid + 5, NULL, 0); +} + +char *gettime() +{ + static char retbuf[60]; + struct timeval tv; + gettimeofday(&tv, NULL); + snprintf(retbuf, sizeof(retbuf), "%lld.%lld", + (long long) tv.tv_sec, (long long) tv.tv_usec); + return retbuf; +} + +int actually_do_unlink = 1; +#define FAST_FLAG_PATH "/var/run/qubes/fast_block_attach" +void set_fast_flag() +{ + int fd = open(FAST_FLAG_PATH, O_CREAT | O_RDONLY, 0600); + if (fd < 0) { + perror("set_fast_flag"); + exit(1); + } + close(fd); +} + +void rm_fast_flag() +{ + if (actually_do_unlink) + unlink(FAST_FLAG_PATH); +} + +#define BUFSIZE (512*1024) +void do_read(int fd) +{ + static char buf[BUFSIZE]; + int n; + while ((n = read(fd, buf, BUFSIZE))) { + if (n < 0) { + perror("read savefile"); + exit(1); + } + } +} + +void preload_cache(int fd) +{ + signal(SIGCHLD, SIG_IGN); + switch (fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + actually_do_unlink = 0; + do_read(fd); + fprintf(stderr, "time=%s, fs cache preload complete\n", + gettime()); + exit(0); + default: + close(fd); + } +} + +int xend_connect() +{ + struct sockaddr_un server; + int s; + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + perror("socket af_unix"); + exit(1); + } + server.sun_family = AF_UNIX; + strcpy(server.sun_path, "/var/run/xend/xmlrpc.sock"); + if (connect + (s, (struct sockaddr *) &server, + strlen(server.sun_path) + sizeof(server.sun_family))) { + perror("connext xend"); + exit(1); + } + return s; +} + +void start_guid(int domid, int argc, char **argv) +{ + int i; + char dstr[40]; + char *guid_args[argc + 2]; + snprintf(dstr, sizeof(dstr), "%d", domid); + guid_args[0] = "qubes_guid"; + guid_args[1] = "-d"; + guid_args[2] = dstr; + for (i = 2; i < argc; i++) + guid_args[i + 1] = argv[i]; + guid_args[argc + 1] = NULL; + execv("/usr/bin/qubes_guid", guid_args); + perror("execv"); +} + +void fix_savefile(int fd, char *buf, char *pattern, char *val) +{ + int i, len = strlen(val), origlen; + char *bracket; + char *loc = strstr(buf + 20, pattern) + strlen(pattern); + if (!loc) + return; + bracket = index(loc, ')'); + if (!bracket) + return; + origlen = (long) bracket - (long) loc; + if (origlen < len) { + fprintf(stderr, "too long string %s\n", val); + exit(1); + } + for (i = 0; i < origlen - len; i++) + loc[i] = ' '; + memcpy(loc + i, val, strlen(val)); + lseek(fd, (long) loc - (long) buf, SEEK_SET); + write(fd, loc, origlen); +} + + +char * dispname_by_dispid(int dispid) +{ + static char retbuf[16]; + snprintf(retbuf, sizeof(retbuf), "disp%d", dispid); + return retbuf; +} + +#define NAME_PATTERN "/root-cow.img" +char *fix_savefile_and_get_vmname(int fd, int dispid) +{ + static char buf[4096]; + char *name; + char *slash; + char val[256]; + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) { + perror("read savefile"); + exit(1); + } + buf[sizeof(buf) - 1] = 0; + snprintf(val, sizeof(val), + "064cd14c-95ad-4fc2-a4c9-cf9f522e5b%02x", dispid); + fix_savefile(fd, buf, "(uuid ", val); + fix_savefile(fd, buf, "(name ", dispname_by_dispid(dispid)); + snprintf(val, sizeof(val), "00:16:3e:7c:8b:%02x", dispid); + fix_savefile(fd, buf, "(mac ", val); + lseek(fd, 0, SEEK_SET); + name = strstr(buf + 20, NAME_PATTERN); + if (!name) { + fprintf(stderr, + "cannot find 'root-cow.img' in savefile\n"); + exit(1); + } + *name = 0; + slash = name - 1; + while (slash[0] && slash[0] != '/') + slash--; + if (!*slash) { + fprintf(stderr, "cannot find / in savefile\n"); + exit(1); + } + return slash + 1; +} + +void unpack_cows(char *name) +{ + char vmdir[4096]; + char tarfile[4096]; + int status; + snprintf(vmdir, sizeof(vmdir), "/var/lib/qubes/appvms/%s", name); + snprintf(tarfile, sizeof(tarfile), + "/var/lib/qubes/appvms/%s/saved_cows.tar", name); + switch (fork()) { + case -1: + fprintf(stderr, "fork"); + exit(1); + case 0: + execl("/bin/tar", "tar", "-C", vmdir, "-Sxf", + tarfile, NULL); + perror("execl"); + exit(1); + default: + wait(&status); + if (WEXITSTATUS(status)) { + fprintf(stderr, "tar exited with status=0x%x\n", + status); + exit(1); + } + fprintf(stderr, "time=%s, cows restored\n", gettime()); + + } +} + +void write_xs_single(struct xs_handle *xs, int domid, char *name, + char *val) +{ + char key[256]; + snprintf(key, sizeof(key), "/local/domain/%d/%s", domid, name); + if (!xs_write(xs, XBT_NULL, key, val, strlen(val))) { + fprintf(stderr, "xs_write"); + exit(1); + } +} + + +void setup_xenstore(int domid, char *name) +{ + char val[256]; + char netvm_id_path[256]; + int fd, n; + char netvm_id[256]; + struct xs_handle *xs = xs_daemon_open(); + if (!xs) { + perror("xs_daemon_open"); + exit(1); + } + snprintf(netvm_id_path, sizeof(netvm_id_path), + "/var/lib/qubes/appvms/%s/netvm_id.txt", name); + fd = open(netvm_id_path, O_RDONLY); + if (fd < 0) { + perror("open netvm_id"); + exit(1); + } + n = read(fd, netvm_id, sizeof(netvm_id) - 1); + close(fd); + netvm_id[n] = 0; + + snprintf(val, sizeof(val), "10.%s.%d.%d", netvm_id, + domid / 254 + 200, (domid % 254) + 1); + write_xs_single(xs, domid, "qubes_ip", val); + write_xs_single(xs, domid, "qubes_netmask", "255.255.0.0"); + snprintf(val, sizeof(val), "10.%s.0.1", netvm_id); + write_xs_single(xs, domid, "qubes_gateway", val); + snprintf(val, sizeof(val), "10.%s.255.254", netvm_id); + write_xs_single(xs, domid, "qubes_secondary_dns", val); + write_xs_single(xs, domid, "qubes_vm_type", "AppVM"); + xs_daemon_close(xs); + +} + +int get_next_disposable_id() +{ + int seq = 0; + int fd = open("/var/run/qubes/dispVM_seq", O_RDWR); + if (fd < 0) { + perror("open dispVM_seq"); + exit(1); + } + read(fd, &seq, sizeof(seq)); + seq++; + lseek(fd, 0, SEEK_SET); + write(fd, &seq, sizeof(seq)); + close(fd); + return seq; +} + +void write_varrun_domid(int domid, char * dispname, char *orig) +{ + FILE *f = fopen("/var/run/qubes/dispVM_xid", "w"); + if (!f) { + perror("fopen dispVM_xid"); + exit(1); + } + fprintf(f, "%d\n%s\n%s\n", domid, dispname, orig); + fclose(f); +} + + +void redirect_stderr() +{ + int fd = + open("/var/log/qubes/qubes_restore.log", + O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (fd < 0) { + syslog(LOG_DAEMON | LOG_ERR, "open qubes_restore.log"); + exit(1); + } + dup2(fd, 2); +} + + +int main(int argc, char **argv) +{ + int fd, domid, dispid; + char *resp; + char *name; + if (argc < 2) { + fprintf(stderr, "usage: %s savefile [guid args] \n", + argv[0]); + exit(1); + } + redirect_stderr(); + fprintf(stderr, "time=%s, starting\n", gettime()); + set_fast_flag(); + atexit(rm_fast_flag); + fd = open(argv[1], O_RDWR); + if (fd < 0) { + perror("open savefile"); + exit(1); + } + dispid = get_next_disposable_id(); + name = fix_savefile_and_get_vmname(fd, dispid); +// printf("name=%s\n", name); + unpack_cows(name); +// no preloading for now, assume savefile in shm +// preload_cache(fd); + fd = xend_connect(); + send_req_restore(fd, argv[1]); + resp = recv_resp(fd); + domid = parse_resp(resp); + write_varrun_domid(domid, dispname_by_dispid(dispid), name); + fprintf(stderr, + "time=%s, created domid=%d, executing set_mem 400\n", + gettime(), domid); + fd = xend_connect(); + send_req_setmem(fd, domid, 400); + resp = recv_resp(fd); +// printf("%s\n", resp); + fprintf(stderr, "time=%s, creating xenstore entries\n", gettime()); + setup_xenstore(domid, name); + fprintf(stderr, "time=%s, starting qubes_guid\n", gettime()); + rm_fast_flag(); + start_guid(domid, argc, argv); + return 0; +} diff --git a/dom0/restore/qvm-create-default-dvm b/dom0/restore/qvm-create-default-dvm new file mode 100755 index 00000000..5750b63f --- /dev/null +++ b/dom0/restore/qvm-create-default-dvm @@ -0,0 +1,35 @@ +#!/bin/sh +if [ $# != 1 -a $# != 2 ] ; then + echo Usage: qvm-create-default-dvm templatename [script-name] + exit 1 +fi +if ! [ -d "/var/lib/qubes/vm-templates/$1" ] ; then + echo /var/lib/qubes/vm-templates/$1 is not a directory + exit 1 +fi +DVMTMPL="$1"-dvm +if ! [ -d "/var/lib/qubes/appvms/$DVMTMPL" ] ; then + if ! qvm-create -t "$1" -l red "$DVMTMPL" ; then exit 1 ; fi +fi +if ! /usr/lib/qubes/qubes_prepare_saved_domain.sh \ + "$DVMTMPL" "/var/lib/qubes/appvms/$DVMTMPL/dvm-savefile" $2 ; then + exit 1 +fi +ROOT=/var/lib/qubes/dvmdata/savefile_root +DEFAULT=/var/lib/qubes/dvmdata/default_savefile +CURRENT=/var/run/qubes/current_savefile +SHMDIR=/dev/shm/qubes +SHMCOPY=$SHMDIR/current_savefile +rm -f $ROOT $DEFAULT $CURRENT +ln -s "/var/lib/qubes/appvms/$DVMTMPL/dvm-savefile" $DEFAULT +ln -s "/var/lib/qubes/vm-templates/$1/root.img" $ROOT +if [ -f /var/lib/qubes/dvmdata/dont_use_shm ] ; then + ln -s $DEFAULT $CURRENT +else + mkdir -m 770 $SHMDIR 2>/dev/null + chgrp qubes $SHMDIR 2>/dev/null + cp $DEFAULT $SHMCOPY || exit 1 + chgrp qubes $SHMCOPY + chmod 660 $SHMCOPY + ln -s $SHMCOPY $CURRENT +fi diff --git a/dom0/restore/xenstore-watch.c b/dom0/restore/xenstore-watch.c new file mode 100644 index 00000000..2e622d8b --- /dev/null +++ b/dom0/restore/xenstore-watch.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +main(int argc, char **argv) +{ + struct xs_handle *xs; + unsigned int count; + char **vec; + char dummy; + if (argc != 2) { + fprintf(stderr, "usage: %s xenstore_path\n", argv[0]); + exit(1); + } + xs = xs_daemon_open(); + if (!xs) { + perror("xs_daemon_open"); + exit(1); + } + if (!xs_watch(xs, argv[1], &dummy)) { + perror("xs_watch"); + exit(1); + } + vec = xs_read_watch(xs, &count); + free(vec); + vec = xs_read_watch(xs, &count); + free(vec); +} diff --git a/netvm/qubes_core b/netvm/qubes_core index d63fbe2a..aebf4d7c 100755 --- a/netvm/qubes_core +++ b/netvm/qubes_core @@ -20,15 +20,17 @@ start() # Setup gateway for all the VMs this netVM is serviceing... brctl addbr br0 + brctl stp br0 off + brctl setfd br0 1 gateway=$(/usr/bin/xenstore-read qubes_netvm_gateway) netmask=$(/usr/bin/xenstore-read qubes_netvm_netmask) network=$(/usr/bin/xenstore-read qubes_netvm_network) secondary_dns=$(/usr/bin/xenstore-read qubes_netvm_secondary_dns) ifconfig br0 $gateway netmask $netmask up ifconfig br0:1 $secondary_dns netmask $netmask - echo "NS1=$gateway" > /var/run/qubes_ns - echo "NS2=$secondary_dns" >> /var/run/qubes_ns - qubes_setup_dnat_to_ns + echo "NS1=$gateway" > /var/run/qubes/qubes_ns + 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 #now no need for dnsmasq # dnsmasq --listen-address $gateway --bind-interfaces diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index 75ce8530..a4444f0b 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -33,6 +33,7 @@ License: GPL URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read Requires: fedora-release = 13 +Requires: /usr/bin/mimeopen Provides: qubes-core-vm %define _builddir %(pwd)/appvm @@ -60,9 +61,12 @@ 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_add_pendrive_script qubes_penctl qvm-copy-to-vm qvm-copy-to-vm.kde $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 +ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} -cp qvm-copy.desktop $RPM_BUILD_ROOT/%{kde_service_dir} +cp qvm-copy.desktop qvm-dvm.desktop $RPM_BUILD_ROOT/%{kde_service_dir} mkdir -p $RPM_BUILD_ROOT/etc/udev/rules.d cp qubes.rules $RPM_BUILD_ROOT/etc/udev/rules.d mkdir -p $RPM_BUILD_ROOT/etc/sysconfig @@ -77,6 +81,12 @@ cp ../common/qubes_serial_login $RPM_BUILD_ROOT/sbin mkdir -p $RPM_BUILD_ROOT/etc cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ +mkdir -p $RPM_BUILD_ROOT/etc/X11 +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 + %triggerin -- initscripts cp /var/lib/qubes/serial.conf /etc/init/serial.conf @@ -174,10 +184,13 @@ rm -rf $RPM_BUILD_ROOT /etc/fstab /etc/init.d/qubes_core /usr/bin/qvm-copy-to-vm -/usr/bin/qvm-copy-to-vm.kde +/usr/lib/qubes/qvm-copy-to-vm.kde +%attr(4755,root,root) /usr/bin/qvm-open-in-dvm +/usr/lib/qubes/qvm-dvm-transfer %{kde_service_dir}/qvm-copy.desktop -%attr(4755,root,root) /usr/bin/qubes_penctl -/usr/bin/qubes_add_pendrive_script +%{kde_service_dir}/qvm-dvm.desktop +%attr(4755,root,root) /usr/lib/qubes/qubes_penctl +/usr/lib/qubes/qubes_add_pendrive_script /etc/udev/rules.d/qubes.rules /etc/sysconfig/iptables /var/lib/qubes @@ -186,3 +199,7 @@ rm -rf $RPM_BUILD_ROOT %dir /mnt/removable /etc/yum.repos.d/qubes.repo /sbin/qubes_serial_login +/usr/bin/qubes_timestamp +%dir /home_volatile +%attr(700,user,user) /home_volatile/user +/etc/X11/xorg-preload-apps.conf diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 753f93cf..ef2d1ed5 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -46,6 +46,7 @@ The Qubes core files for installation on Dom0. %build python -m compileall qvm-core python -O -m compileall qvm-core +make -C restore %install @@ -58,6 +59,9 @@ cp qvm-tools/qvm-* $RPM_BUILD_ROOT/usr/bin cp clipboard_notifier/qclipd $RPM_BUILD_ROOT/usr/bin cp pendrive_swapper/qfilexchgd $RPM_BUILD_ROOT/usr/bin +mkdir -p $RPM_BUILD_ROOT/etc/xen/scripts +cp restore/block.qubes $RPM_BUILD_ROOT/etc/xen/scripts + mkdir -p $RPM_BUILD_ROOT%{python_sitearch}/qubes cp qvm-core/qubes.py $RPM_BUILD_ROOT%{python_sitearch}/qubes cp qvm-core/qubes.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes @@ -74,11 +78,16 @@ cp aux-tools/create_apps_for_appvm.sh $RPM_BUILD_ROOT/usr/lib/qubes cp aux-tools/remove_appvm_appmenus.sh $RPM_BUILD_ROOT/usr/lib/qubes cp pendrive_swapper/qubes_pencmd $RPM_BUILD_ROOT/usr/lib/qubes +cp restore/xenstore-watch restore/qvm-create-default-dvm $RPM_BUILD_ROOT/usr/bin +cp restore/qubes_restore $RPM_BUILD_ROOT/usr/lib/qubes +cp restore/qubes_prepare_saved_domain.sh $RPM_BUILD_ROOT/usr/lib/qubes + mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/vm-templates mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/appvms mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/backup +mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/dvmdata mkdir -p $RPM_BUILD_ROOT/usr/share/qubes/icons cp icons/*.png $RPM_BUILD_ROOT/usr/share/qubes/icons @@ -87,9 +96,9 @@ mkdir -p $RPM_BUILD_ROOT/etc/yum.repos.d cp ../dom0/qubes.repo $RPM_BUILD_ROOT/etc/yum.repos.d mkdir -p $RPM_BUILD_ROOT/usr/bin -cp ../common/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/bin +cp ../common/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/etc/dhclient.d -ln -s /usr/bin/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/etc/dhclient.d/qubes_setup_dnat_to_ns.sh +ln -s /usr/lib/qubes/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/etc/dhclient.d/qubes_setup_dnat_to_ns.sh mkdir -p $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ cp ../common/qubes_nmhook $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ mkdir -p $RPM_BUILD_ROOT/etc/sysconfig @@ -99,6 +108,9 @@ mkdir -p $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d cp pm-utils/01qubes-sync-vms-clock $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/ cp pm-utils/02qubes-pause-vms $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/ +%triggerin -- xen-runtime +sed -i 's/\/block /\/block.qubes /' /etc/udev/rules.d/xen-backend.rules + %post if [ -e /etc/yum.repos.d/qubes-r1-dom0.repo ]; then @@ -167,6 +179,7 @@ if [ "$1" = 0 ] ; then chgrp root /etc/xen chmod 700 /etc/xen groupdel qubes + sed -i 's/\/block.qubes /\/block /' /etc/udev/rules.d/xen-backend.rules fi %files @@ -194,11 +207,17 @@ fi %attr(770,root,qubes) %dir /var/lib/qubes/vm-templates %attr(770,root,qubes) %dir /var/lib/qubes/appvms %attr(770,root,qubes) %dir /var/lib/qubes/backup +%attr(770,root,qubes) %dir /var/lib/qubes/dvmdata %dir /usr/share/qubes/icons/*.png /etc/yum.repos.d/qubes.repo -/usr/bin/qubes_setup_dnat_to_ns +/usr/lib/qubes/qubes_setup_dnat_to_ns /etc/dhclient.d/qubes_setup_dnat_to_ns.sh /etc/NetworkManager/dispatcher.d/qubes_nmhook /etc/sysconfig/iptables /usr/lib64/pm-utils/sleep.d/01qubes-sync-vms-clock /usr/lib64/pm-utils/sleep.d/02qubes-pause-vms +/usr/bin/xenstore-watch +/usr/bin/qvm-create-default-dvm +/usr/lib/qubes/qubes_restore +/usr/lib/qubes/qubes_prepare_saved_domain.sh +/etc/xen/scripts/block.qubes diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index 4081dbb6..5d130e1b 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -59,10 +59,10 @@ cp fstab $RPM_BUILD_ROOT/etc/fstab 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 ../common/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/bin +mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes +cp ../common/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/etc/dhclient.d -ln -s /usr/bin/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/etc/dhclient.d/qubes_setup_dnat_to_ns.sh +ln -s /usr/lib/qubes/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/etc/dhclient.d/qubes_setup_dnat_to_ns.sh mkdir -p $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ cp ../common/qubes_nmhook $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ mkdir -p $RPM_BUILD_ROOT/etc/yum.repos.d @@ -71,6 +71,7 @@ mkdir -p $RPM_BUILD_ROOT/sbin cp ../common/qubes_serial_login $RPM_BUILD_ROOT/sbin mkdir -p $RPM_BUILD_ROOT/etc cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ +mkdir -p $RPM_BUILD_ROOT/var/run/qubes %triggerin -- initscripts cp /var/lib/qubes/serial.conf /etc/init/serial.conf @@ -168,8 +169,9 @@ rm -rf $RPM_BUILD_ROOT /etc/sysconfig/iptables /etc/init.d/qubes_core /var/lib/qubes -/usr/bin/qubes_setup_dnat_to_ns +/usr/lib/qubes/qubes_setup_dnat_to_ns /etc/dhclient.d/qubes_setup_dnat_to_ns.sh /etc/NetworkManager/dispatcher.d/qubes_nmhook /etc/yum.repos.d/qubes.repo /sbin/qubes_serial_login +%dir /var/run/qubes