From 84b1a186ffe485cb6f9f605c54b0b09a53a601cd Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 15 Mar 2011 16:43:43 +0100 Subject: [PATCH] Added qfile-unpacker and qfile-daemon --- appvm/Makefile | 4 +- appvm/qfile-unpacker.c | 83 +++++++++++++++++++++++++++++++ appvm/unpack.c | 101 ++++++++++++++++++++++++++++++++++++++ dom0/restore/qfile-daemon | 59 ++++++++++++++++++++++ rpm_spec/core-appvm.spec | 3 +- rpm_spec/core-dom0.spec | 2 + 6 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 appvm/qfile-unpacker.c create mode 100644 appvm/unpack.c create mode 100644 dom0/restore/qfile-daemon diff --git a/appvm/Makefile b/appvm/Makefile index c6f4d256..c1abcfad 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,12 +1,14 @@ CC=gcc CFLAGS=-Wall -I../common -all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm qfile-agent +all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm qfile-agent qfile-unpacker dvm_file_editor: dvm_file_editor.o ../common/ioall.o $(CC) -o dvm_file_editor dvm_file_editor.o ../common/ioall.o qfile-agent-dvm: qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o $(CC) -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o qfile-agent: qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o $(CC) -o qfile-agent qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o +qfile-unpacker: qfile-unpacker.o ../common/ioall.o ../common/gui-fatal.o copy_file.o unpack.o + $(CC) -o qfile-unpacker qfile-unpacker.o ../common/ioall.o ../common/gui-fatal.o copy_file.o unpack.o qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o diff --git a/appvm/qfile-unpacker.c b/appvm/qfile-unpacker.c new file mode 100644 index 00000000..eaa5c067 --- /dev/null +++ b/appvm/qfile-unpacker.c @@ -0,0 +1,83 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "filecopy.h" +#define INCOMING_DIR_ROOT "/home/user/incoming" +int prepare_creds_return_uid(char *username) +{ + struct passwd *pwd; + pwd = getpwnam(username); + if (!pwd) { + perror("getpwnam"); + exit(1); + } + setenv("HOME", pwd->pw_dir, 1); + setenv("USER", username, 1); + setgid(pwd->pw_gid); + initgroups(username, pwd->pw_gid); + setfsuid(pwd->pw_uid); + return pwd->pw_uid; +} + +void wait_for_child(int statusfd) +{ + int status; + if (read(statusfd, &status, sizeof status)!=sizeof status) + gui_fatal("File copy error: Internal error reading status from unpacker"); + errno = status; + switch (status) { + case LEGAL_EOF: break; + case 0: gui_fatal("File copy: Connection terminated unexpectedly"); break; + case EINVAL: gui_fatal("File copy: Corrupted data from packer"); break; + case EEXIST: gui_fatal("File copy: not overwriting existing file. Clean ~/incoming, and retry copy"); break; + default: gui_fatal("File copy"); + } +} + +extern void do_unpack(int); + +int main(int argc, char ** argv) +{ + char *incoming_dir; + int pipefds[2]; + int uid; + + pipe(pipefds); + + uid = prepare_creds_return_uid("user"); + + mkdir(INCOMING_DIR_ROOT, 0700); + asprintf(&incoming_dir, "%s/from-%s", INCOMING_DIR_ROOT, argv[1]); + mkdir(incoming_dir, 0700); + if (chdir(incoming_dir)) + gui_fatal("Error chdir to %s", incoming_dir); + switch (fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + if (chroot(incoming_dir)) //impossible + gui_fatal("Error chroot to %s", incoming_dir); + setuid(uid); + close(pipefds[0]); + do_unpack(pipefds[1]); + exit(0); + default:; + } + + setuid(uid); + close(pipefds[1]); + wait_for_child(pipefds[0]); + + return 0; +} diff --git a/appvm/unpack.c b/appvm/unpack.c new file mode 100644 index 00000000..c0353c11 --- /dev/null +++ b/appvm/unpack.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "filecopy.h" + +char namebuf[MAX_PATH_LENGTH]; +void notify_progress(int p1, int p2) +{ +} + +int global_status_fd; +void do_exit(int code) +{ + int codebuf = code; + write(global_status_fd, &codebuf, sizeof codebuf); + exit(0); +} + + +void fix_times_and_perms(struct file_header *hdr, char *name) +{ + struct timeval times[2] = + { {hdr->atime, hdr->atime_nsec / 1000}, {hdr->mtime, + hdr->mtime_nsec / 1000} + }; + if (chmod(name, hdr->mode & 07777)) + do_exit(errno); + if (utimes(name, times)) + do_exit(errno); +} + + + +void process_one_file_reg(struct file_header *hdr, char *name) +{ + char *ret; + int fdout = + open(name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0700); + if (fdout < 0) + do_exit(errno); + ret = copy_file(fdout, 0, hdr->filelen); + if (ret) + do_exit(errno); + close(fdout); + fix_times_and_perms(hdr, name); +} + + +void process_one_file_dir(struct file_header *hdr, char *name) +{ + if (mkdir(name, 0700) && errno != EEXIST) + do_exit(errno); + fix_times_and_perms(hdr, name); +} + +void process_one_file_link(struct file_header *hdr, char *name) +{ + char content[MAX_PATH_LENGTH]; + if (hdr->filelen > MAX_PATH_LENGTH - 1) + do_exit(ENAMETOOLONG); + if (!read_all(0, content, hdr->filelen)) + do_exit(errno); + content[hdr->filelen] = 0; + if (symlink(content, name)) + do_exit(errno); + +} + +void process_one_file(struct file_header *hdr) +{ + if (hdr->namelen > MAX_PATH_LENGTH - 1) + do_exit(ENAMETOOLONG); + if (!read_all(0, namebuf, hdr->namelen)) + do_exit(errno); + namebuf[hdr->namelen] = 0; + if (S_ISREG(hdr->mode)) + process_one_file_reg(hdr, namebuf); + else if (S_ISLNK(hdr->mode)) + process_one_file_link(hdr, namebuf); + else if (S_ISDIR(hdr->mode)) + process_one_file_dir(hdr, namebuf); + else + do_exit(EINVAL); +} + +void do_unpack(int fd) +{ + global_status_fd = fd; + struct file_header hdr; + while (read_all(0, &hdr, sizeof hdr)) + process_one_file(&hdr); + if (errno) + do_exit(errno); + else + do_exit(LEGAL_EOF); +} diff --git a/dom0/restore/qfile-daemon b/dom0/restore/qfile-daemon new file mode 100644 index 00000000..6b589279 --- /dev/null +++ b/dom0/restore/qfile-daemon @@ -0,0 +1,59 @@ +#!/usr/bin/python2.6 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Rafal Wojtczuk +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# +import os +import sys +import subprocess +from qubes.qubes import QubesVmCollection + +def is_copy_allowed(vm): +# if vm.copy_allowed: +# return True + q = 'Do you authorize file copy from ' + q+= os.getenv("QREXEC_REMOTE_DOMAIN") + q+= ' to ' + vm.name + ' ?' + retcode = subprocess.call(['/usr/bin/kdialog', '--yesno', q, '--title', 'File transfer confirmation']) + return retcode == 0 + +def main(): + FILECOPY_VMNAME_SIZE = 32 + blob=os.read(0, FILECOPY_VMNAME_SIZE) + vmname = blob.split("\x00")[0] + + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + vm = qvm_collection.get_vm_by_name(vmname) +# we do not want to flood dom0 with error windows; so just log to stderr + if vm is None: + print >> sys.stderr, 'Domain ' + vmname + ' does not exist ?' + exit(1) + if not vm.is_running(): + print >> sys.stderr, 'Domain ' + vmname + ' is not running ?' + exit(1) + if not is_copy_allowed(vm): + exit(1) + cmd = "root:/usr/lib/qubes/qfile-unpacker " + os.getenv("QREXEC_REMOTE_DOMAIN") + os.execl("/usr/lib/qubes/qrexec_client", "qrexec_client", "-d", vmname, cmd) + +main() diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index 3023ffa0..f3c875d4 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -76,7 +76,7 @@ cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm qvm-open-in-dvm2 $RPM_BUILD_RO mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes -cp dvm_file_editor qfile-agent qfile-agent-dvm $RPM_BUILD_ROOT/usr/lib/qubes +cp dvm_file_editor qfile-agent qfile-agent-dvm qfile-unpacker $RPM_BUILD_ROOT/usr/lib/qubes ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} @@ -218,6 +218,7 @@ rm -rf $RPM_BUILD_ROOT /usr/lib/qubes/qrexec_agent /usr/lib/qubes/qfile-agent /usr/lib/qubes/qfile-agent-dvm +/usr/lib/qubes/qfile-unpacker /etc/udev/rules.d/qubes.rules /etc/sysconfig/iptables /var/lib/qubes diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 89cbd284..ed72a347 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -96,6 +96,7 @@ cp restore/xenstore-watch restore/qvm-create-default-dvm $RPM_BUILD_ROOT/usr/bin cp restore/qubes_restore restore/xenfreepages $RPM_BUILD_ROOT/usr/lib/qubes cp restore/qubes_prepare_saved_domain.sh $RPM_BUILD_ROOT/usr/lib/qubes cp restore/qfile-daemon-dvm $RPM_BUILD_ROOT/usr/lib/qubes +cp restore/qfile-daemon $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/vm-templates @@ -265,6 +266,7 @@ fi /usr/lib/qubes/qmemman_daemon.py* /usr/lib/qubes/meminfo-writer /usr/lib/qubes/qfile-daemon-dvm* +/usr/lib/qubes/qfile-daemon %attr(770,root,qubes) %dir /var/lib/qubes %attr(770,root,qubes) %dir /var/lib/qubes/vm-templates %attr(770,root,qubes) %dir /var/lib/qubes/appvms