diff --git a/Makefile b/Makefile index b38c5f5..7034995 100644 --- a/Makefile +++ b/Makefile @@ -33,3 +33,5 @@ clean: (cd dom0/restore && make clean) (cd dom0/qmemman && make clean) (cd common && make clean) + make -C qrexec clean + make -C vchan clean diff --git a/appvm/Makefile b/appvm/Makefile index 858a665..f95b1ab 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,6 +1,14 @@ CC=gcc -CFLAGS=-Wall -all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm +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) -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) -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) -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) -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 @@ -9,4 +17,5 @@ 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 xenstore-watch *.o *~ + rm -f qfile-agent-dvm qfile-agent qfile-unpacker dvm_file_editor qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm *.o *~ + rm -f xenstore-watch diff --git a/appvm/copy_file.c b/appvm/copy_file.c new file mode 100644 index 0000000..5f7fc79 --- /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/dvm2.h b/appvm/dvm2.h new file mode 100644 index 0000000..0e5922c --- /dev/null +++ b/appvm/dvm2.h @@ -0,0 +1,2 @@ +#define DVM_FILENAME_SIZE 256 +#define DVM_SPOOL "/home/user/.dvmspool" diff --git a/appvm/dvm_file_editor.c b/appvm/dvm_file_editor.c new file mode 100644 index 0000000..4eed30b --- /dev/null +++ b/appvm/dvm_file_editor.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include +#include "dvm2.h" + +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_file(char *filename) +{ + int fd = open(filename, O_WRONLY | O_CREAT, 0600); + if (fd < 0) { + perror("open file"); + exit(1); + } + if (!copy_fd_all(fd, 0)) + exit(1); + close(fd); +} + +void send_file_back(char * filename) +{ + int fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("open file"); + exit(1); + } + if (!copy_fd_all(1, fd)) + exit(1); + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dvm2.h" + +void send_file(char *fname) +{ + char *base; + int fd = open(fname, O_RDONLY); + if (fd < 0) + gui_fatal("open %s", fname); + 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)) + gui_fatal("send filename to dispVM"); + if (!copy_fd_all(1, fd)) + gui_fatal("send file to dispVM"); + close(1); +} + +int copy_and_return_nonemptiness(int tmpfd) +{ + struct stat st; + if (!copy_fd_all(tmpfd, 0)) + gui_fatal("receiving file from dispVM"); + if (fstat(tmpfd, &st)) + gui_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) + gui_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); + gui_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)) + gui_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)) + gui_fatal("bad dvm_entry"); + entry_size = st.st_size; + filename = calloc(1, entry_size + DVM_FILENAME_SIZE); + if (!filename) + gui_fatal("malloc"); + if (!read_all(entry_fd, filename, entry_size)) + gui_fatal("read dvm entry %s", abs_spool_entry_name); + close(entry_fd); + talk_to_daemon(filename); +} + +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 (!strcmp(fname, ".") || !strcmp(fname, "..")) + continue; + process_spoolentry(fname); + break; + } + closedir(dir); +} + +int main() +{ + signal(SIGPIPE, SIG_IGN); + scan_spool(DVM_SPOOL); + return 0; +} diff --git a/appvm/qfile-agent.c b/appvm/qfile-agent.c new file mode 100644 index 0000000..f7e27a9 --- /dev/null +++ b/appvm/qfile-agent.c @@ -0,0 +1,215 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "filecopy.h" + +enum { + PROGRESS_FLAG_NORMAL, + PROGRESS_FLAG_INIT, + PROGRESS_FLAG_DONE +}; + + + +char *client_flags; +void do_notify_progress(long long total, int flag) +{ + FILE *progress; + if (!client_flags[0]) + return; + progress = fopen(client_flags, "w"); + if (!progress) + return; + fprintf(progress, "%d %lld %s", getpid(), total, + flag == PROGRESS_FLAG_DONE ? "DONE" : "BUSY"); + fclose(progress); +} + +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 + || (flag != PROGRESS_FLAG_NORMAL)) { + do_notify_progress(total, flag); + 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, PROGRESS_FLAG_INIT); + send_vmname(vmname); + while ((entry = get_item(data, ¤t, datasize))) { + 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)) + gui_fatal("chdir to %s", entry); + do_fs_walk(sep + 1); + } + notify_progress(0, PROGRESS_FLAG_DONE); +} + +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/appvm/qfile-unpacker.c b/appvm/qfile-unpacker.c new file mode 100644 index 0000000..eaa5c06 --- /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/qubes_core_appvm b/appvm/qubes_core_appvm index ed250dd..25ff7f4 100755 --- a/appvm/qubes_core_appvm +++ b/appvm/qubes_core_appvm @@ -55,6 +55,7 @@ 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/appvm/qvm-copy-to-vm2 b/appvm/qvm-copy-to-vm2 new file mode 100755 index 0000000..56dcdef --- /dev/null +++ b/appvm/qvm-copy-to-vm2 @@ -0,0 +1,46 @@ +#!/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. +# +# + +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 diff --git a/appvm/qvm-copy-to-vm2.kde b/appvm/qvm-copy-to-vm2.kde new file mode 100755 index 0000000..879279b --- /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 5795eb6..4d5e800 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 diff --git a/appvm/qvm-dvm.desktop b/appvm/qvm-dvm.desktop index a7f5ad7..67f9ea5 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 0000000..eb0d4e2 --- /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/appvm/unpack.c b/appvm/unpack.c new file mode 100644 index 0000000..ad53ebf --- /dev/null +++ b/appvm/unpack.c @@ -0,0 +1,105 @@ +#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) +{ +// 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); +} + +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/common/gui-fatal.c b/common/gui-fatal.c new file mode 100644 index 0000000..ed2b3d4 --- /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 0000000..de9799f --- /dev/null +++ b/common/gui-fatal.h @@ -0,0 +1,2 @@ +void gui_fatal(const char *fmt, ...); +void gui_nonfatal(const char *fmt, ...); diff --git a/common/ioall.c b/common/ioall.c new file mode 100644 index 0000000..239f333 --- /dev/null +++ b/common/ioall.c @@ -0,0 +1,97 @@ +/* + * 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 + +void perror_wrapper(char * msg) +{ + int prev=errno; + perror(msg); + errno=prev; +} + + +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 == -1 && errno == EINTR) + continue; + if (ret <= 0) { + perror_wrapper("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 == -1 && errno == EINTR) + continue; + if (ret == 0) { + errno = 0; + fprintf(stderr, "EOF\n"); + return 0; + } + if (ret < 0) { + perror_wrapper("read"); + return 0; + } + got_read += ret; + } +// 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 == -1 && errno == EINTR) + continue; + if (!ret) + break; + if (ret < 0) { + perror_wrapper("read"); + return 0; + } + if (!write_all(fdout, buf, ret)) { + perror_wrapper("write"); + return 0; + } + } + return 1; +} diff --git a/common/ioall.h b/common/ioall.h new file mode 100644 index 0000000..1a700c6 --- /dev/null +++ b/common/ioall.h @@ -0,0 +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); diff --git a/netvm/qubes_core_netvm b/netvm/qubes_core_netvm index dd713fc..2268c01 100755 --- a/netvm/qubes_core_netvm +++ b/netvm/qubes_core_netvm @@ -32,6 +32,8 @@ 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 "" return 0 diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index 56c62ba..d36f6af 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -60,6 +60,9 @@ mkdir -p $RPM_BUILD_ROOT/var/lib/qubes %build make clean all make -C ../common +make -C ../qrexec +make -C ../vchan +make -C ../u2mfn %install @@ -67,9 +70,13 @@ mkdir -p $RPM_BUILD_ROOT/etc/init.d cp qubes_core_appvm $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 +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 cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} @@ -86,6 +93,18 @@ 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 + +mkdir -p $RPM_BUILD_ROOT/var/run/qubes + +%triggerin -- initscripts +cp /var/lib/qubes/serial.conf /etc/init/serial.conf + %post chkconfig --add qubes_core_appvm || echo "WARNING: Cannot add service qubes_core!" @@ -111,14 +130,22 @@ rm -rf $RPM_BUILD_ROOT %defattr(-,root,root,-) /etc/init.d/qubes_core_appvm /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 /usr/lib/qubes/meminfo-writer +/usr/lib/qubes/dvm_file_editor %{kde_service_dir}/qvm-copy.desktop %{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 +/usr/lib/qubes/qfile-agent +/usr/lib/qubes/qfile-agent-dvm +/usr/lib/qubes/qfile-unpacker /etc/udev/rules.d/qubes.rules %dir /mnt/incoming %dir /mnt/outgoing @@ -127,3 +154,20 @@ 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 +%dir /var/run/qubes + + +%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-netvm.spec b/rpm_spec/core-netvm.spec index 97af562..c970acd 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -45,6 +45,9 @@ The Qubes core files for installation inside a Qubes NetVM. %pre %build +make -C ../qrexec +make -C ../vchan +make -C ../u2mfn %install @@ -53,6 +56,7 @@ 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 @@ -63,6 +67,10 @@ 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 @@ -83,6 +91,7 @@ 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 @@ -90,3 +99,6 @@ rm -rf $RPM_BUILD_ROOT /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