diff --git a/misc/uca_qubes.xml b/misc/uca_qubes.xml index 2e4ad8d..64a3ce9 100644 --- a/misc/uca_qubes.xml +++ b/misc/uca_qubes.xml @@ -59,7 +59,7 @@ document-open - Open in DisposableVM + Edit in DisposableVM 1507455559234996-8 /usr/lib/qubes/qvm-actions.sh opendvm %F @@ -70,3 +70,16 @@ + + document-open + View in DisposableVM + 1507455559234997-9 + /usr/lib/qubes/qvm-actions.sh viewdvm %F + + * + + + + + + diff --git a/qubes-rpc/dvm2.h b/qubes-rpc/dvm2.h index 0e5922c..72b73f7 100644 --- a/qubes-rpc/dvm2.h +++ b/qubes-rpc/dvm2.h @@ -1,2 +1,3 @@ #define DVM_FILENAME_SIZE 256 #define DVM_SPOOL "/home/user/.dvmspool" +#define DVM_VIEW_ONLY_PREFIX "view-only-" diff --git a/qubes-rpc/gui-fatal.c b/qubes-rpc/gui-fatal.c index 5900ce5..d50db09 100644 --- a/qubes-rpc/gui-fatal.c +++ b/qubes-rpc/gui-fatal.c @@ -10,58 +10,58 @@ static void fix_display(void) { - setenv("DISPLAY", ":0", 1); + setenv("DISPLAY", ":0", 1); } static void produce_message(const char * type, const char *fmt, va_list args) { - char *dialog_msg; - char buf[1024]; - (void) vsnprintf(buf, sizeof(buf), fmt, args); - if (asprintf(&dialog_msg, "%s: %s: %s (error type: %s)", - program_invocation_short_name, type, buf, strerror(errno)) < 0) { - fprintf(stderr, "Failed to allocate memory for error message :(\n"); - return; - } - fprintf(stderr, "%s\n", dialog_msg); - switch (fork()) { - case -1: - exit(1); //what else - case 0: - if (geteuid() == 0) - if (setuid(getuid()) != 0) - perror("setuid failed, calling kdialog/zenity as root"); - fix_display(); + char *dialog_msg; + char buf[1024]; + (void) vsnprintf(buf, sizeof(buf), fmt, args); + if (asprintf(&dialog_msg, "%s: %s: %s (error type: %s)", + program_invocation_short_name, type, buf, strerror(errno)) < 0) { + fprintf(stderr, "Failed to allocate memory for error message :(\n"); + return; + } + fprintf(stderr, "%s\n", dialog_msg); + switch (fork()) { + case -1: + exit(1); //what else + case 0: + if (geteuid() == 0) + if (setuid(getuid()) != 0) + perror("setuid failed, calling kdialog/zenity as root"); + fix_display(); #ifdef USE_KDIALOG - execlp("/usr/bin/kdialog", "kdialog", "--sorry", dialog_msg, NULL); + execlp("/usr/bin/kdialog", "kdialog", "--sorry", dialog_msg, NULL); #else - execlp("/usr/bin/zenity", "zenity", "--error", "--text", dialog_msg, NULL); + execlp("/usr/bin/zenity", "zenity", "--error", "--text", dialog_msg, NULL); #endif - exit(1); - default:; - } - free(dialog_msg); + exit(1); + default:; + } + free(dialog_msg); } void gui_fatal(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - produce_message("Fatal error", fmt, args); - va_end(args); - exit(1); + va_list args; + va_start(args, fmt); + produce_message("Fatal error", fmt, args); + va_end(args); + exit(1); } void qfile_gui_fatal(const char *fmt, va_list args) { - produce_message("Fatal error", fmt, args); + produce_message("Fatal error", fmt, args); exit(1); } void gui_nonfatal(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - produce_message("Information", fmt, args); - va_end(args); + va_list args; + va_start(args, fmt); + produce_message("Information", fmt, args); + va_end(args); } diff --git a/qubes-rpc/qfile-agent.c b/qubes-rpc/qfile-agent.c index de65788..42a1dfd 100644 --- a/qubes-rpc/qfile-agent.c +++ b/qubes-rpc/qfile-agent.c @@ -13,107 +13,107 @@ #include enum { - PROGRESS_FLAG_NORMAL, - PROGRESS_FLAG_INIT, - PROGRESS_FLAG_DONE + PROGRESS_FLAG_NORMAL, + PROGRESS_FLAG_INIT, + PROGRESS_FLAG_DONE }; void do_notify_progress(long long total, int flag) { - const char *du_size_env = getenv("FILECOPY_TOTAL_SIZE"); - const char *progress_type_env = getenv("PROGRESS_TYPE"); - const char *saved_stdout_env = getenv("SAVED_FD_1"); - int ignore; - if (!progress_type_env) - return; - if (!strcmp(progress_type_env, "console") && du_size_env) { - char msg[256]; - snprintf(msg, sizeof(msg), "sent %lld/%lld KB\r", - total / 1024, strtoull(du_size_env, NULL, 0)); - ignore = write(2, msg, strlen(msg)); - if (flag == PROGRESS_FLAG_DONE) - ignore = write(2, "\n", 1); - } - if (!strcmp(progress_type_env, "gui") && saved_stdout_env) { - char msg[256]; - snprintf(msg, sizeof(msg), "%lld\n", total); - ignore = write(strtoul(saved_stdout_env, NULL, 0), msg, - strlen(msg)); - } - if (ignore < 0) { - /* silence gcc warning */ - } + const char *du_size_env = getenv("FILECOPY_TOTAL_SIZE"); + const char *progress_type_env = getenv("PROGRESS_TYPE"); + const char *saved_stdout_env = getenv("SAVED_FD_1"); + int ignore; + if (!progress_type_env) + return; + if (!strcmp(progress_type_env, "console") && du_size_env) { + char msg[256]; + snprintf(msg, sizeof(msg), "sent %lld/%lld KB\r", + total / 1024, strtoull(du_size_env, NULL, 0)); + ignore = write(2, msg, strlen(msg)); + if (flag == PROGRESS_FLAG_DONE) + ignore = write(2, "\n", 1); + } + if (!strcmp(progress_type_env, "gui") && saved_stdout_env) { + char msg[256]; + snprintf(msg, sizeof(msg), "%lld\n", total); + ignore = write(strtoul(saved_stdout_env, NULL, 0), msg, + strlen(msg)); + } + if (ignore < 0) { + /* silence gcc warning */ + } } 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)) { - // check for possible error from qfile-unpacker; if error occured, - // exit() will be called, so don't bother with current state - // (notify_progress can be called as callback from copy_file()) - if (flag == PROGRESS_FLAG_NORMAL) - wait_for_result(); - do_notify_progress(total, flag); - prev_total = total; - } + static long long total = 0; + static long long prev_total = 0; + total += size; + if (total > prev_total + PROGRESS_NOTIFY_DELTA + || (flag != PROGRESS_FLAG_NORMAL)) { + // check for possible error from qfile-unpacker; if error occured, + // exit() will be called, so don't bother with current state + // (notify_progress can be called as callback from copy_file()) + if (flag == PROGRESS_FLAG_NORMAL) + wait_for_result(); + do_notify_progress(total, flag); + prev_total = total; + } } char *get_abs_path(const char *cwd, const char *pathname) { - char *ret; - if (pathname[0] == '/') - return strdup(pathname); - if (asprintf(&ret, "%s/%s", cwd, pathname) < 0) - return NULL; - else - return ret; + char *ret; + if (pathname[0] == '/') + return strdup(pathname); + if (asprintf(&ret, "%s/%s", cwd, pathname) < 0) + return NULL; + else + return ret; } int main(int argc, char **argv) { - int i; - char *entry; - char *cwd; - char *sep; - int ignore_symlinks = 0; + int i; + char *entry; + char *cwd; + char *sep; + int ignore_symlinks = 0; - qfile_pack_init(); - register_error_handler(qfile_gui_fatal); - register_notify_progress(¬ify_progress); - notify_progress(0, PROGRESS_FLAG_INIT); - cwd = getcwd(NULL, 0); - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "--ignore-symlinks")==0) { - ignore_symlinks = 1; - continue; - } + qfile_pack_init(); + register_error_handler(qfile_gui_fatal); + register_notify_progress(¬ify_progress); + notify_progress(0, PROGRESS_FLAG_INIT); + cwd = getcwd(NULL, 0); + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "--ignore-symlinks")==0) { + ignore_symlinks = 1; + continue; + } - entry = get_abs_path(cwd, argv[i]); + entry = get_abs_path(cwd, argv[i]); - do { - sep = rindex(entry, '/'); - if (!sep) - gui_fatal - ("Internal error: nonabsolute filenames not allowed"); - *sep = 0; - } while (sep[1] == 0); - if (entry[0] == 0) { - if (chdir("/") < 0) { - gui_fatal("Internal error: chdir(\"/\") failed?!"); - } - } else if (chdir(entry)) - gui_fatal("chdir to %s", entry); - do_fs_walk(sep + 1, ignore_symlinks); - free(entry); - } - notify_end_and_wait_for_result(); - notify_progress(0, PROGRESS_FLAG_DONE); - return 0; + do { + sep = rindex(entry, '/'); + if (!sep) + gui_fatal + ("Internal error: nonabsolute filenames not allowed"); + *sep = 0; + } while (sep[1] == 0); + if (entry[0] == 0) { + if (chdir("/") < 0) { + gui_fatal("Internal error: chdir(\"/\") failed?!"); + } + } else if (chdir(entry)) + gui_fatal("chdir to %s", entry); + do_fs_walk(sep + 1, ignore_symlinks); + free(entry); + } + notify_end_and_wait_for_result(); + notify_progress(0, PROGRESS_FLAG_DONE); + return 0; } diff --git a/qubes-rpc/qfile-unpacker.c b/qubes-rpc/qfile-unpacker.c index 86a9527..817277a 100644 --- a/qubes-rpc/qfile-unpacker.c +++ b/qubes-rpc/qfile-unpacker.c @@ -17,81 +17,81 @@ #define INCOMING_DIR_ROOT "/home/user/QubesIncoming" int prepare_creds_return_uid(const char *username) { - const struct passwd *pwd; - pwd = getpwnam(username); - if (!pwd) { - perror("getpwnam"); - exit(1); - } - setenv("HOME", pwd->pw_dir, 1); - setenv("USER", username, 1); - if (setgid(pwd->pw_gid) < 0) - gui_fatal("Error setting group permissions"); - if (initgroups(username, pwd->pw_gid) < 0) - gui_fatal("Error initializing groups"); - if (setfsuid(pwd->pw_uid) < 0) - gui_fatal("Error setting filesystem level permissions"); - return pwd->pw_uid; + const struct passwd *pwd; + pwd = getpwnam(username); + if (!pwd) { + perror("getpwnam"); + exit(1); + } + setenv("HOME", pwd->pw_dir, 1); + setenv("USER", username, 1); + if (setgid(pwd->pw_gid) < 0) + gui_fatal("Error setting group permissions"); + if (initgroups(username, pwd->pw_gid) < 0) + gui_fatal("Error initializing groups"); + if (setfsuid(pwd->pw_uid) < 0) + gui_fatal("Error setting filesystem level permissions"); + return pwd->pw_uid; } int main(int argc __attribute((__unused__)), char ** argv __attribute__((__unused__))) { - char *incoming_dir; - int uid, ret; - pid_t pid; - const char *remote_domain; - char *procdir_path; - int procfs_fd; + char *incoming_dir; + int uid, ret; + pid_t pid; + const char *remote_domain; + char *procdir_path; + int procfs_fd; - uid = prepare_creds_return_uid("user"); + uid = prepare_creds_return_uid("user"); - remote_domain = getenv("QREXEC_REMOTE_DOMAIN"); - if (!remote_domain) { - gui_fatal("Cannot get remote domain name"); - exit(1); - } - mkdir(INCOMING_DIR_ROOT, 0700); - if (asprintf(&incoming_dir, "%s/%s", INCOMING_DIR_ROOT, remote_domain) < 0) - gui_fatal("Error allocating memory"); - mkdir(incoming_dir, 0700); - if (chdir(incoming_dir)) - gui_fatal("Error chdir to %s", incoming_dir); + remote_domain = getenv("QREXEC_REMOTE_DOMAIN"); + if (!remote_domain) { + gui_fatal("Cannot get remote domain name"); + exit(1); + } + mkdir(INCOMING_DIR_ROOT, 0700); + if (asprintf(&incoming_dir, "%s/%s", INCOMING_DIR_ROOT, remote_domain) < 0) + gui_fatal("Error allocating memory"); + mkdir(incoming_dir, 0700); + if (chdir(incoming_dir)) + gui_fatal("Error chdir to %s", incoming_dir); - if (mount(".", ".", NULL, MS_BIND | MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0) - gui_fatal("Failed to mount a directory %s", incoming_dir); + if (mount(".", ".", NULL, MS_BIND | MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0) + gui_fatal("Failed to mount a directory %s", incoming_dir); - /* parse the input in unprivileged child process, parent will hold root - * access to unmount incoming dir */ - switch (pid=fork()) { - case -1: - gui_fatal("Failed to create new process"); - case 0: - if (asprintf(&procdir_path, "/proc/%d/fd", getpid()) < 0) { - gui_fatal("Error allocating memory"); - } - procfs_fd = open(procdir_path, O_DIRECTORY | O_RDONLY); - if (procfs_fd < 0) - perror("Failed to open /proc"); - else - set_procfs_fd(procfs_fd); - free(procdir_path); + /* parse the input in unprivileged child process, parent will hold root + * access to unmount incoming dir */ + switch (pid=fork()) { + case -1: + gui_fatal("Failed to create new process"); + case 0: + if (asprintf(&procdir_path, "/proc/%d/fd", getpid()) < 0) { + gui_fatal("Error allocating memory"); + } + procfs_fd = open(procdir_path, O_DIRECTORY | O_RDONLY); + if (procfs_fd < 0) + perror("Failed to open /proc"); + else + set_procfs_fd(procfs_fd); + free(procdir_path); - if (chroot(".")) - gui_fatal("Error chroot to %s", incoming_dir); - if (setuid(uid) < 0) { - /* no kdialog inside chroot */ - perror("setuid"); - exit(1); - } - return do_unpack(); - } - if (waitpid(pid, &ret, 0) < 0) { - gui_fatal("Failed to wait for child process"); - } - if (umount2(".", MNT_DETACH) < 0) - gui_fatal("Cannot umount incoming directory"); - if (!WIFEXITED(ret)) { - gui_fatal("Child process exited abnormally"); - } - return WEXITSTATUS(ret); + if (chroot(".")) + gui_fatal("Error chroot to %s", incoming_dir); + if (setuid(uid) < 0) { + /* no kdialog inside chroot */ + perror("setuid"); + exit(1); + } + return do_unpack(); + } + if (waitpid(pid, &ret, 0) < 0) { + gui_fatal("Failed to wait for child process"); + } + if (umount2(".", MNT_DETACH) < 0) + gui_fatal("Cannot umount incoming directory"); + if (!WIFEXITED(ret)) { + gui_fatal("Child process exited abnormally"); + } + return WEXITSTATUS(ret); } diff --git a/qubes-rpc/qopen-in-vm.c b/qubes-rpc/qopen-in-vm.c index 43cf854..5e796a5 100644 --- a/qubes-rpc/qopen-in-vm.c +++ b/qubes-rpc/qopen-in-vm.c @@ -9,100 +9,124 @@ #include #include #include +#include #include #include "dvm2.h" -void send_file(const char *fname) +void send_file(const char *fname, int view_only) { - const char *base; - char sendbuf[DVM_FILENAME_SIZE]; - 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; - strncpy(sendbuf,base,DVM_FILENAME_SIZE - 1); /* fills out with NULs */ - sendbuf[DVM_FILENAME_SIZE - 1] = '\0'; - if (!write_all(1, sendbuf, DVM_FILENAME_SIZE)) - gui_fatal("send filename to dispVM"); - if (!copy_fd_all(1, fd)) - gui_fatal("send file to dispVM"); - close(1); - close(fd); + const char *base; + char sendbuf[DVM_FILENAME_SIZE] = {0}; + size_t sendbuf_size = DVM_FILENAME_SIZE; + int fd = open(fname, O_RDONLY); + if (fd < 0) + gui_fatal("open %s", fname); + if (view_only) { + strncpy(sendbuf, DVM_VIEW_ONLY_PREFIX, sendbuf_size); + sendbuf_size -= strlen(DVM_VIEW_ONLY_PREFIX); + } + base = rindex(fname, '/'); + if (!base) + base = fname; + else + base++; + if (strlen(base) >= sendbuf_size) + base += strlen(base) - sendbuf_size + 1; + strncat(sendbuf,base,sendbuf_size - 1); /* fills out with NULs */ + sendbuf[DVM_FILENAME_SIZE - 1] = '\0'; + if (!write_all(1, sendbuf, DVM_FILENAME_SIZE)) + gui_fatal("send filename to dispVM"); + if (!copy_fd_all(1, fd)) + gui_fatal("send file to dispVM"); + close(1); + close(fd); } 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); + 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 > 0; + return st.st_size > 0; } void recv_file_nowrite(const char *fname) { - char *tempfile; - char *errmsg; - int tmpfd = -1; + char *tempfile; + char *errmsg; + int tmpfd = -1; - if (asprintf(&tempfile, "/tmp/file_edited_in_dvm.XXXXXX") != -1) - tmpfd = mkstemp(tempfile); - if (tmpfd < 0) - gui_fatal("unable to create any temporary file, aborting"); - if (!copy_and_return_nonemptiness(tmpfd)) { - unlink(tempfile); - return; - } - if (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) != -1) + if (asprintf(&tempfile, "/tmp/file_edited_in_dvm.XXXXXX") != -1) + tmpfd = mkstemp(tempfile); + if (tmpfd < 0) + gui_fatal("unable to create any temporary file, aborting"); + if (!copy_and_return_nonemptiness(tmpfd)) { + unlink(tempfile); + return; + } + if (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) != -1) gui_nonfatal(errmsg); } void actually_recv_file(const char *fname, const char *tempfile, int tmpfd) { - if (!copy_and_return_nonemptiness(tmpfd)) { - unlink(tempfile); - return; - } - if (rename(tempfile, fname)) - gui_fatal("rename"); + if (!copy_and_return_nonemptiness(tmpfd)) { + unlink(tempfile); + return; + } + if (rename(tempfile, fname)) + gui_fatal("rename"); } void recv_file(const char *fname) { - int tmpfd = -1; - char *tempfile; - if (asprintf(&tempfile, "%s.XXXXXX", fname) != -1) { - tmpfd = mkstemp(tempfile); - } - if (tmpfd < 0) - recv_file_nowrite(fname); - else - actually_recv_file(fname, tempfile, tmpfd); -} - -void talk_to_daemon(const char *fname) -{ - send_file(fname); - recv_file(fname); + int tmpfd = -1; + char *tempfile; + if (asprintf(&tempfile, "%s.XXXXXX", fname) != -1) { + tmpfd = mkstemp(tempfile); + } + if (tmpfd < 0) + recv_file_nowrite(fname); + else + actually_recv_file(fname, tempfile, tmpfd); } int main(int argc, char ** argv) { - signal(SIGPIPE, SIG_IGN); - if (argc!=2) - gui_fatal("OpenInVM - no file given?"); - talk_to_daemon(argv[1]); - return 0; + char *fname; + int view_only = 0; + int ret; + const struct option opts[] = { + {"view-only", no_argument, &view_only, 1}, + {0} + }; + + while ((ret=getopt_long(argc, argv, "", opts, NULL)) != -1) { + if (ret == '?') { + exit(2); + } + } + + signal(SIGPIPE, SIG_IGN); + + if (optind >= argc) + gui_fatal("OpenInVM - no file given?"); + fname = argv[optind]; + send_file(fname, view_only); + if (!view_only) { + recv_file(fname); + } else { + /* discard received data */ + int null_fd = open("/dev/null", O_WRONLY); + copy_fd_all(null_fd, 0); + close(null_fd); + } + return 0; } diff --git a/qubes-rpc/qvm-actions.sh b/qubes-rpc/qvm-actions.sh index 691b3f2..3e4a968 100755 --- a/qubes-rpc/qvm-actions.sh +++ b/qubes-rpc/qvm-actions.sh @@ -45,6 +45,12 @@ case "$action" in qvm-open-in-dvm "$file" | zenity --notification --text "Opening $file in DisposableVM..." --timeout 3 & done ;; + viewdvm) + for file in "$@" + do + qvm-open-in-dvm --view-only "$file" | zenity --notification --text "Opening $file in DisposableVM..." --timeout 3 & + done + ;; *) echo "Unknown action. Aborting..." exit 1 diff --git a/qubes-rpc/qvm-dvm.desktop b/qubes-rpc/qvm-dvm.desktop index ba34250..e293b01 100644 --- a/qubes-rpc/qvm-dvm.desktop +++ b/qubes-rpc/qvm-dvm.desktop @@ -1,10 +1,15 @@ [Desktop Entry] -Actions=QvmDvm; +Actions=QvmDvm;QvmViewDvm Type=Service X-KDE-ServiceTypes=KonqPopupMenu/Plugin,all/allfiles [Desktop Action QvmDvm] Exec=/usr/bin/qvm-open-in-dvm %U Icon=kget -Name=Open In DisposableVM +Name=Edit In DisposableVM + +[Desktop Action QvmViewDvm] +Exec=/usr/bin/qvm-open-in-dvm --view-only %U +Icon=kget +Name=View In DisposableVM diff --git a/qubes-rpc/qvm-open-in-dvm b/qubes-rpc/qvm-open-in-dvm index 84af921..8046858 100755 --- a/qubes-rpc/qvm-open-in-dvm +++ b/qubes-rpc/qvm-open-in-dvm @@ -20,10 +20,10 @@ # # -if ! [ $# = 1 ] ; then - echo "Usage: $0 filename" +if ! [ $# = 1 ] && ! [ $# = 2 ]; then + echo "Usage: $0 [--view-only] filename" exit 1 fi # shellcheck disable=SC2016 -exec qvm-open-in-vm '$dispvm' "$1" +exec qvm-open-in-vm '$dispvm' "$@" diff --git a/qubes-rpc/qvm-open-in-vm b/qubes-rpc/qvm-open-in-vm index d109e6b..ead9acf 100755 --- a/qubes-rpc/qvm-open-in-vm +++ b/qubes-rpc/qvm-open-in-vm @@ -20,16 +20,37 @@ # # -if ! [ $# = 2 ] ; then - echo "Usage: $0 vmname filename" - exit 1 +usage() { + echo "Usage: $0 [--view-only] vmname filename" + exit 2 +} + +qopen_opts= +target= +filename= + +while [ $# -gt 0 ]; do + if [ "x$1" = "x--view-only" ]; then + qopen_opts=--view-only + elif [ -z "$target" ]; then + target="$1" + elif [ -z "$filename" ]; then + filename="$1" + else + usage + fi + shift +done + +if [ -z "$target" ] || [ -z "$filename" ]; then + usage fi -case "$2" in +case "$filename" in *://*) - exec /usr/lib/qubes/qrexec-client-vm "$1" qubes.OpenURL /bin/echo "$2" + exec /usr/lib/qubes/qrexec-client-vm "$target" qubes.OpenURL /bin/echo "$filename" ;; *) - exec /usr/lib/qubes/qrexec-client-vm "$1" qubes.OpenInVM "/usr/lib/qubes/qopen-in-vm" "$2" + exec /usr/lib/qubes/qrexec-client-vm "$target" qubes.OpenInVM "/usr/lib/qubes/qopen-in-vm" $qopen_opts "$filename" ;; esac diff --git a/qubes-rpc/qvm_dvm_nautilus.py b/qubes-rpc/qvm_dvm_nautilus.py index a4311a4..7614396 100755 --- a/qubes-rpc/qvm_dvm_nautilus.py +++ b/qubes-rpc/qvm_dvm_nautilus.py @@ -17,15 +17,24 @@ class OpenInDvmItemExtension(GObject.GObject, Nautilus.MenuProvider): if not files: return - menu_item = Nautilus.MenuItem(name='QubesMenuProvider::OpenInDvm', - label='Open In DisposableVM', + menu_item1 = Nautilus.MenuItem(name='QubesMenuProvider::OpenInDvm', + label='Edit In DisposableVM', tip='', icon='') - menu_item.connect('activate', self.on_menu_item_clicked, files) - return menu_item, + menu_item1.connect('activate', self.on_menu_item_clicked, files) - def on_menu_item_clicked(self, menu, files): + menu_item2 = Nautilus.MenuItem(name='QubesMenuProvider::ViewInDvm', + label='View In DisposableVM', + tip='', + icon='') + + menu_item2.connect('activate', + self.on_menu_item_clicked, + files, True) + return menu_item1, menu_item2, + + def on_menu_item_clicked(self, menu, files, view_only=False): '''Called when user chooses files though Nautilus context menu. ''' for file_obj in files: @@ -38,6 +47,11 @@ class OpenInDvmItemExtension(GObject.GObject, Nautilus.MenuProvider): # Use subprocess.DEVNULL in python >= 3.3 devnull = open(os.devnull, 'wb') + command = ['nohup', '/usr/bin/qvm-open-in-dvm'] + if view_only: + command.append('--view-only') + command.append(gio_file.get_path()) # Use Popen instead of subprocess.call to spawn the process - Popen(['nohup', '/usr/bin/qvm-open-in-dvm', gio_file.get_path()], stdout=devnull, stderr=devnull) + Popen(command, stdout=devnull, stderr=devnull) + devnull.close() diff --git a/qubes-rpc/vm-file-editor.c b/qubes-rpc/vm-file-editor.c index 55594d2..45004e8 100644 --- a/qubes-rpc/vm-file-editor.c +++ b/qubes-rpc/vm-file-editor.c @@ -19,217 +19,227 @@ static const char *cleanup_dirname = NULL; static void cleanup_file(void) { - if (cleanup_filename) { - if (unlink(cleanup_filename) < 0) - fprintf(stderr, "Failed to remove file at exit\n"); - cleanup_filename = NULL; - } - if (cleanup_dirname) { - if (rmdir(cleanup_dirname) < 0) - fprintf(stderr, "Failed to remove directory at exit\n"); - cleanup_dirname = NULL; - } + if (cleanup_filename) { + if (unlink(cleanup_filename) < 0) + fprintf(stderr, "Failed to remove file at exit\n"); + cleanup_filename = NULL; + } + if (cleanup_dirname) { + if (rmdir(cleanup_dirname) < 0) + fprintf(stderr, "Failed to remove directory at exit\n"); + cleanup_dirname = NULL; + } } const char *gettime(void) { - static char retbuf[60]; - struct timeval tv; - gettimeofday(&tv, NULL); - snprintf(retbuf, sizeof(retbuf), "%lld.%06lld", - (long long) tv.tv_sec, (long long) tv.tv_usec); - return retbuf; + static char retbuf[60]; + struct timeval tv; + gettimeofday(&tv, NULL); + snprintf(retbuf, sizeof(retbuf), "%lld.%06lld", + (long long) tv.tv_sec, (long long) tv.tv_usec); + return retbuf; } static char *get_directory(void) { - const char *remote_domain; - char *dir; - size_t len; - char *ret; + const char *remote_domain; + char *dir; + size_t len; + char *ret; - remote_domain = getenv("QREXEC_REMOTE_DOMAIN"); - if (!remote_domain) { - fprintf(stderr, "Cannot get remote domain name\n"); - exit(1); - } - if (!*remote_domain || index(remote_domain, '/')) - goto fail; - if (!strcmp(remote_domain, ".") || !strcmp(remote_domain, "..")) - goto fail; + remote_domain = getenv("QREXEC_REMOTE_DOMAIN"); + if (!remote_domain) { + fprintf(stderr, "Cannot get remote domain name\n"); + exit(1); + } + if (!*remote_domain || index(remote_domain, '/')) + goto fail; + if (!strcmp(remote_domain, ".") || !strcmp(remote_domain, "..")) + goto fail; - len = strlen("/tmp/-XXXXXX")+strlen(remote_domain)+1; - dir = malloc(len); - if (!dir) { - fprintf(stderr, "Cannot allocate memory\n"); - exit(1); - } - snprintf(dir, len, "/tmp/%s-XXXXXX", remote_domain); + len = strlen("/tmp/-XXXXXX")+strlen(remote_domain)+1; + dir = malloc(len); + if (!dir) { + fprintf(stderr, "Cannot allocate memory\n"); + exit(1); + } + snprintf(dir, len, "/tmp/%s-XXXXXX", remote_domain); - ret = mkdtemp(dir); - if (ret == NULL) { - perror("mkdtemp"); - exit(1); - } - cleanup_dirname = strdup(ret); - return ret; + ret = mkdtemp(dir); + if (ret == NULL) { + perror("mkdtemp"); + exit(1); + } + cleanup_dirname = strdup(ret); + return ret; fail: - fprintf(stderr, "Invalid remote domain name: %s\n", remote_domain); - exit(1); + fprintf(stderr, "Invalid remote domain name: %s\n", remote_domain); + exit(1); } -char *get_filename(void) +char *get_filename(int *view_only) { - char buf[DVM_FILENAME_SIZE]; - static char *retname; - int i; - char *directory; - size_t len; + char buf[DVM_FILENAME_SIZE]; + char *fname = buf; + static char *retname; + int i; + char *directory; + size_t len; - directory = get_directory(); - if (!read_all(0, buf, sizeof(buf))) - exit(1); - buf[DVM_FILENAME_SIZE-1] = 0; - if (index(buf, '/')) { - fprintf(stderr, "filename contains /"); - exit(1); - } - for (i=0; buf[i]!=0; i++) { - // replace some characters with _ (eg mimeopen have problems with some of them) - if (index(" !?\"#$%^&*()[]<>;`~|", buf[i])) - buf[i]='_'; - } - len = strlen(directory)+1+strlen(buf)+1; - retname = malloc(len); - if (!retname) { - fprintf(stderr, "Cannot allocate memory\n"); - exit(1); - } - snprintf(retname, len, "%s/%s", directory, buf); - free(directory); - return retname; + directory = get_directory(); + if (!read_all(0, buf, sizeof(buf))) + exit(1); + buf[DVM_FILENAME_SIZE-1] = 0; + if (index(buf, '/')) { + fprintf(stderr, "filename contains /"); + exit(1); + } + for (i=0; buf[i]!=0; i++) { + // replace some characters with _ (eg mimeopen have problems with some of them) + if (index(" !?\"#$%^&*()[]<>;`~|", buf[i])) + buf[i]='_'; + } + if (strncmp(buf, DVM_VIEW_ONLY_PREFIX, strlen(DVM_VIEW_ONLY_PREFIX)) == 0) { + *view_only = 1; + fname += strlen(DVM_VIEW_ONLY_PREFIX); + } + len = strlen(directory)+1+strlen(fname)+1; + retname = malloc(len); + if (!retname) { + fprintf(stderr, "Cannot allocate memory\n"); + exit(1); + } + snprintf(retname, len, "%s/%s", directory, fname); + free(directory); + return retname; } void copy_file_by_name(const char *filename) { - int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) { - perror("open file"); - exit(1); - } - /* we now have created a new file, ensure we delete it at the end */ - cleanup_filename = strdup(filename); - atexit(cleanup_file); - if (!copy_fd_all(fd, 0)) + int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (fd < 0) { + perror("open file"); exit(1); - close(fd); + } + /* we now have created a new file, ensure we delete it at the end */ + cleanup_filename = strdup(filename); + atexit(cleanup_file); + if (!copy_fd_all(fd, 0)) + exit(1); + close(fd); } void send_file_back(const 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); - close(1); + int fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("open file"); + exit(1); + } + if (!copy_fd_all(1, fd)) + exit(1); + close(fd); + close(1); } int main() { - struct stat stat_pre, stat_post, session_stat; - char *filename = get_filename(); - int child, status, log_fd, null_fd; - FILE *waiter_pidfile; + struct stat stat_pre, stat_post, session_stat; + int view_only = 0; + char *filename = get_filename(&view_only); + int child, status, log_fd, null_fd; + FILE *waiter_pidfile; - copy_file_by_name(filename); - if (stat(filename, &stat_pre)) { - perror("stat pre"); - exit(1); - } + copy_file_by_name(filename); + if (view_only) { + // mark file as read-only so applications will signal it to the user + chmod(filename, 0400); + } + if (stat(filename, &stat_pre)) { + perror("stat pre"); + exit(1); + } #ifdef DEBUG - fprintf(stderr, "time=%s, waiting for qubes-session\n", gettime()); + fprintf(stderr, "time=%s, waiting for qubes-session\n", gettime()); #endif - // wait for X server to starts (especially in DispVM) - if (stat("/tmp/qubes-session-env", &session_stat)) { - switch (child = fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - waiter_pidfile = fopen("/tmp/qubes-session-waiter", "a"); - if (waiter_pidfile == NULL) { - perror("fopen waiter_pidfile"); - exit(1); - } - fprintf(waiter_pidfile, "%d\n", getpid()); - fclose(waiter_pidfile); - // check the second time, to prevent race - if (stat("/tmp/qubes-session-env", &session_stat)) { - // wait for qubes-session notify - pause(); - } - exit(0); - default: - waitpid(child, &status, 0); - if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { - //propagate exit code from child - exit(WEXITSTATUS(status)); - } - } - } + // wait for X server to starts (especially in DispVM) + if (stat("/tmp/qubes-session-env", &session_stat)) { + switch (child = fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + waiter_pidfile = fopen("/tmp/qubes-session-waiter", "a"); + if (waiter_pidfile == NULL) { + perror("fopen waiter_pidfile"); + exit(1); + } + fprintf(waiter_pidfile, "%d\n", getpid()); + fclose(waiter_pidfile); + // check the second time, to prevent race + if (stat("/tmp/qubes-session-env", &session_stat)) { + // wait for qubes-session notify + pause(); + } + exit(0); + default: + waitpid(child, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { + //propagate exit code from child + exit(WEXITSTATUS(status)); + } + } + } #ifdef DEBUG - fprintf(stderr, "time=%s, starting editor\n", gettime()); + fprintf(stderr, "time=%s, starting editor\n", gettime()); #endif - switch (child = fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - null_fd = open("/dev/null", O_RDONLY); - dup2(null_fd, 0); - close(null_fd); + switch (child = fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + null_fd = open("/dev/null", O_RDONLY); + dup2(null_fd, 0); + close(null_fd); - log_fd = open("/tmp/mimeopen.log", O_CREAT | O_APPEND, 0666); - if (log_fd == -1) { - perror("open /tmp/mimeopen.log"); - exit(1); - } - dup2(log_fd, 1); - close(log_fd); + log_fd = open("/tmp/mimeopen.log", O_CREAT | O_APPEND, 0666); + if (log_fd == -1) { + perror("open /tmp/mimeopen.log"); + exit(1); + } + dup2(log_fd, 1); + close(log_fd); - setenv("HOME", USER_HOME, 1); - setenv("DISPLAY", ":0", 1); - execl("/usr/bin/qubes-open", "qubes-open", filename, (char*)NULL); - perror("execl"); - exit(1); - default: - waitpid(child, &status, 0); - if (status != 0) { - char cmd[512]; + setenv("HOME", USER_HOME, 1); + setenv("DISPLAY", ":0", 1); + execl("/usr/bin/qubes-open", "qubes-open", filename, (char*)NULL); + perror("execl"); + exit(1); + default: + waitpid(child, &status, 0); + if (status != 0) { + char cmd[512]; #ifdef USE_KDIALOG - snprintf(cmd, sizeof(cmd), - "HOME=/home/user DISPLAY=:0 /usr/bin/kdialog --sorry 'Unable to handle mimetype of the requested file (exit status: %d)!' > /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1