From 486f17ec2d647e797cd12e5ba67b648139c455e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 2 Oct 2017 03:19:30 +0200 Subject: [PATCH 1/4] Add convenient wrappers for qvm-copy-to-vm and qvm-move-to-vm Default `ask` policy ignore target domain specified by the caller, so it doesn't make sense to specify one. Provide convenient wrappers not needing one. Do not change behaviour of existing tools for compatibility reasons. Fixes QubesOS/qubes-issues#3141 --- Makefile | 2 ++ debian/qubes-core-agent.install | 2 ++ qubes-rpc/qvm-copy | 25 +++++++++++++++++++++++++ rpm_spec/core-agent.spec | 2 ++ 4 files changed, 31 insertions(+) create mode 100644 qubes-rpc/qvm-copy diff --git a/Makefile b/Makefile index b656d9a..932bf33 100644 --- a/Makefile +++ b/Makefile @@ -235,7 +235,9 @@ install-common: install-doc install -m 0755 misc/qvm-features-request $(DESTDIR)$(BINDIR)/qvm-features-request install -m 0755 qubes-rpc/qvm-sync-clock $(DESTDIR)$(BINDIR)/qvm-sync-clock install qubes-rpc/{qvm-open-in-dvm,qvm-open-in-vm,qvm-copy-to-vm,qvm-run-vm} $(DESTDIR)/usr/bin + install qubes-rpc/qvm-copy $(DESTDIR)/usr/bin ln -s qvm-copy-to-vm $(DESTDIR)/usr/bin/qvm-move-to-vm + ln -s qvm-copy $(DESTDIR)/usr/bin/qvm-move install qubes-rpc/qvm-copy-to-vm.kde $(DESTDIR)$(LIBDIR)/qubes install qubes-rpc/qvm-copy-to-vm.gnome $(DESTDIR)$(LIBDIR)/qubes install qubes-rpc/qvm-move-to-vm.kde $(DESTDIR)$(LIBDIR)/qubes diff --git a/debian/qubes-core-agent.install b/debian/qubes-core-agent.install index fade43a..1eb21b4 100644 --- a/debian/qubes-core-agent.install +++ b/debian/qubes-core-agent.install @@ -86,8 +86,10 @@ lib/systemd/system/systemd-timesyncd.service.d/30_qubes.conf usr/bin/qubes-desktop-run usr/bin/qubes-open usr/bin/qubes-session-autostart +usr/bin/qvm-copy usr/bin/qvm-copy-to-vm usr/bin/qvm-features-request +usr/bin/qvm-move usr/bin/qvm-move-to-vm usr/bin/qvm-open-in-dvm usr/bin/qvm-open-in-vm diff --git a/qubes-rpc/qvm-copy b/qubes-rpc/qvm-copy new file mode 100644 index 0000000..d77e7a5 --- /dev/null +++ b/qubes-rpc/qvm-copy @@ -0,0 +1,25 @@ +#!/bin/sh +set -e +# +# The Qubes OS Project, https://www.qubes-os.org +# +# Copyright (C) 2017 Marek Marczykowski-Górecki +# +# 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. +# +# + +# shellcheck disable=SC2016 +exec "$0-to-vm" '$default' "$@" diff --git a/rpm_spec/core-agent.spec b/rpm_spec/core-agent.spec index ddce418..159dd1c 100644 --- a/rpm_spec/core-agent.spec +++ b/rpm_spec/core-agent.spec @@ -576,6 +576,8 @@ rm -f %{name}-%{version} /usr/sbin/qubes-serial-login /usr/bin/qvm-copy-to-vm /usr/bin/qvm-move-to-vm +/usr/bin/qvm-copy +/usr/bin/qvm-move /usr/bin/qvm-open-in-dvm /usr/bin/qvm-open-in-vm /usr/bin/qvm-run-vm From 1497b3b05b508bfb4eaa77ca834343823d63e967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 2 Oct 2017 03:59:38 +0200 Subject: [PATCH 2/4] qrexec: code style fix - use spaces for indentation --- qrexec/qrexec-agent-data.c | 4 +- qrexec/qrexec-agent.c | 1 + qrexec/qrexec-fork-server.c | 104 ++++++++++++++++++------------------ 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/qrexec/qrexec-agent-data.c b/qrexec/qrexec-agent-data.c index e2fe134..147670c 100644 --- a/qrexec/qrexec-agent-data.c +++ b/qrexec/qrexec-agent-data.c @@ -152,7 +152,7 @@ void send_exit_code(libvchan_t *data_vchan, int status) */ int handle_input(libvchan_t *vchan, int fd, int msg_type) { - char buf[MAX_DATA_CHUNK]; + char buf[MAX_DATA_CHUNK]; int len; struct msg_header hdr; @@ -201,7 +201,7 @@ int handle_input(libvchan_t *vchan, int fd, int msg_type) int handle_remote_data(libvchan_t *data_vchan, int stdin_fd, int *status, struct buffer *stdin_buf) { - struct msg_header hdr; + struct msg_header hdr; char buf[MAX_DATA_CHUNK]; /* do not receive any data if we have something already buffered */ diff --git a/qrexec/qrexec-agent.c b/qrexec/qrexec-agent.c index 6155c95..fa38ded 100644 --- a/qrexec/qrexec-agent.c +++ b/qrexec/qrexec-agent.c @@ -160,6 +160,7 @@ void do_exec(const char *cmd) strcpy(buf + strlen(QUBES_RPC_MULTIPLEXER_PATH), realcmd + RPC_REQUEST_COMMAND_LEN); realcmd = buf; } + signal(SIGCHLD, SIG_DFL); signal(SIGPIPE, SIG_DFL); diff --git a/qrexec/qrexec-fork-server.c b/qrexec/qrexec-fork-server.c index 681e626..d52b377 100644 --- a/qrexec/qrexec-fork-server.c +++ b/qrexec/qrexec-fork-server.c @@ -35,19 +35,19 @@ void do_exec(const char *cmd) { - char buf[strlen(QUBES_RPC_MULTIPLEXER_PATH) + strlen(cmd) - strlen(RPC_REQUEST_COMMAND) + 1]; - /* replace magic RPC cmd with RPC multiplexer path */ - if (strncmp(cmd, RPC_REQUEST_COMMAND " ", strlen(RPC_REQUEST_COMMAND)+1)==0) { - strcpy(buf, QUBES_RPC_MULTIPLEXER_PATH); - strcpy(buf + strlen(QUBES_RPC_MULTIPLEXER_PATH), cmd + strlen(RPC_REQUEST_COMMAND)); - cmd = buf; - } - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); + char buf[strlen(QUBES_RPC_MULTIPLEXER_PATH) + strlen(cmd) - strlen(RPC_REQUEST_COMMAND) + 1]; + /* replace magic RPC cmd with RPC multiplexer path */ + if (strncmp(cmd, RPC_REQUEST_COMMAND " ", strlen(RPC_REQUEST_COMMAND)+1)==0) { + strcpy(buf, QUBES_RPC_MULTIPLEXER_PATH); + strcpy(buf + strlen(QUBES_RPC_MULTIPLEXER_PATH), cmd + strlen(RPC_REQUEST_COMMAND)); + cmd = buf; + } + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); - execl("/bin/sh", "sh", "-c", cmd, NULL); - perror("execl"); - exit(1); + execl("/bin/sh", "sh", "-c", cmd, NULL); + perror("execl"); + exit(1); } void handle_vchan_error(const char *op) @@ -57,43 +57,43 @@ void handle_vchan_error(const char *op) } void handle_single_command(int fd, struct qrexec_cmd_info *info) { - char cmdline[info->cmdline_len+1]; + char cmdline[info->cmdline_len+1]; - if (!read_all(fd, cmdline, info->cmdline_len)) - return; - cmdline[info->cmdline_len] = 0; + if (!read_all(fd, cmdline, info->cmdline_len)) + return; + cmdline[info->cmdline_len] = 0; - handle_new_process(info->type, info->connect_domain, - info->connect_port, - cmdline, info->cmdline_len); + handle_new_process(info->type, info->connect_domain, + info->connect_port, + cmdline, info->cmdline_len); } int main(int argc, char **argv) { - int s, fd; - char *socket_path; - struct qrexec_cmd_info info; - struct sockaddr_un peer; - unsigned int addrlen; + int s, fd; + char *socket_path; + struct qrexec_cmd_info info; + struct sockaddr_un peer; + unsigned int addrlen; - if (argc == 2) { - socket_path = argv[1]; - } else if (argc == 1) { - /* this will be leaked, but we don't care as the process will then terminate */ - if (asprintf(&socket_path, QREXEC_FORK_SERVER_SOCKET, getenv("USER")) < 0) { - fprintf(stderr, "Memory allocation failed\n"); - exit(1); - } - } else { - fprintf(stderr, "Usage: %s [socket path]\n", argv[0]); - exit(1); - } + if (argc == 2) { + socket_path = argv[1]; + } else if (argc == 1) { + /* this will be leaked, but we don't care as the process will then terminate */ + if (asprintf(&socket_path, QREXEC_FORK_SERVER_SOCKET, getenv("USER")) < 0) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + } else { + fprintf(stderr, "Usage: %s [socket path]\n", argv[0]); + exit(1); + } - s = get_server_socket(socket_path); - if (fcntl(s, F_SETFD, O_CLOEXEC) < 0) { - perror("fcntl"); - exit(1); - } + s = get_server_socket(socket_path); + if (fcntl(s, F_SETFD, O_CLOEXEC) < 0) { + perror("fcntl"); + exit(1); + } /* fork into background */ switch (fork()) { case -1: @@ -104,17 +104,17 @@ int main(int argc, char **argv) { default: exit(0); } - signal(SIGCHLD, SIG_IGN); + signal(SIGCHLD, SIG_IGN); register_exec_func(do_exec); - while ((fd = accept(s, (struct sockaddr *) &peer, &addrlen)) >= 0) { - if (read_all(fd, &info, sizeof(info))) { - handle_single_command(fd, &info); - } - close(fd); - addrlen = sizeof(peer); - } - close(s); - unlink(socket_path); - return 0; + while ((fd = accept(s, (struct sockaddr *) &peer, &addrlen)) >= 0) { + if (read_all(fd, &info, sizeof(info))) { + handle_single_command(fd, &info); + } + close(fd); + addrlen = sizeof(peer); + } + close(s); + unlink(socket_path); + return 0; } From 6bf395022aef4dcf455f31253df526a7c2f1ab4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 2 Oct 2017 04:10:55 +0200 Subject: [PATCH 3/4] qrexec: use user shell instead of hardcoded /bin/sh Fixes QubesOS/qubes-issues#3139 --- qrexec/qrexec-agent.c | 14 +++++++++++++- qrexec/qrexec-fork-server.c | 7 ++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/qrexec/qrexec-agent.c b/qrexec/qrexec-agent.c index fa38ded..7fa2f66 100644 --- a/qrexec/qrexec-agent.c +++ b/qrexec/qrexec-agent.c @@ -144,6 +144,8 @@ void do_exec(const char *cmd) pid_t child, pid; char **env; char pid_s[32]; + char *arg0; + char *shell_basename; #endif if (!realcmd) @@ -184,6 +186,14 @@ void do_exec(const char *cmd) pw->pw_shell = strdup(pw->pw_shell); endpwent(); + shell_basename = basename (pw->pw_shell); + /* this process is going to die shortly, so don't care about freeing */ + arg0 = malloc (strlen (shell_basename) + 2); + if (!arg0) + goto error; + arg0[0] = '-'; + strcpy (arg0 + 1, shell_basename); + retval = pam_start("qrexec", user, &conv, &pamh); if (retval != PAM_SUCCESS) goto error; @@ -220,6 +230,7 @@ void do_exec(const char *cmd) goto error; case 0: /* child */ + if (setgid (pw->pw_gid)) exit(126); if (setuid (pw->pw_uid)) @@ -227,7 +238,8 @@ void do_exec(const char *cmd) setsid(); /* This is a copy but don't care to free as we exec later anyways. */ env = pam_getenvlist (pamh); - execle("/bin/sh", "-sh", "-c", realcmd, (char*)NULL, env); + + execle(pw->pw_shell, arg0, "-c", realcmd, (char*)NULL, env); exit(127); default: /* parent */ diff --git a/qrexec/qrexec-fork-server.c b/qrexec/qrexec-fork-server.c index d52b377..8d53144 100644 --- a/qrexec/qrexec-fork-server.c +++ b/qrexec/qrexec-fork-server.c @@ -35,6 +35,7 @@ void do_exec(const char *cmd) { + char *shell; char buf[strlen(QUBES_RPC_MULTIPLEXER_PATH) + strlen(cmd) - strlen(RPC_REQUEST_COMMAND) + 1]; /* replace magic RPC cmd with RPC multiplexer path */ if (strncmp(cmd, RPC_REQUEST_COMMAND " ", strlen(RPC_REQUEST_COMMAND)+1)==0) { @@ -45,7 +46,11 @@ void do_exec(const char *cmd) signal(SIGCHLD, SIG_DFL); signal(SIGPIPE, SIG_DFL); - execl("/bin/sh", "sh", "-c", cmd, NULL); + shell = getenv("SHELL"); + if (!shell) + shell = "/bin/sh"; + + execl(shell, basename(shell), "-c", cmd, NULL); perror("execl"); exit(1); } From a59ac1b4f958dec263dc605fb8db37e6d915a9b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 2 Oct 2017 19:42:00 +0200 Subject: [PATCH 4/4] qubes.ResizeDisk: handle dmroot being a symlink In non-template-based-VMs it can be just a symlink (depending on initramfs version). --- qubes-rpc/qubes.ResizeDisk | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/qubes-rpc/qubes.ResizeDisk b/qubes-rpc/qubes.ResizeDisk index 0ac0656..038467d 100755 --- a/qubes-rpc/qubes.ResizeDisk +++ b/qubes-rpc/qubes.ResizeDisk @@ -13,15 +13,17 @@ case $disk_name in root) # force some read to refresh device size head /dev/xvda > /dev/null - new_size=$(cat /sys/block/xvda/size) - ro=$(/sys/block/xvda/ro) - if [ "$ro" -eq 1 ]; then - new_table="0 $new_size snapshot /dev/xvda /dev/xvdc2 N 16" - else - new_table="0 $new_size linear /dev/xvda 0" + if [ "$(stat -Lc %t /dev/mapper/dmroot)" != "ca" ]; then + new_size=$(cat /sys/block/xvda/size) + ro=$(/sys/block/xvda/ro) + if [ "$ro" -eq 1 ]; then + new_table="0 $new_size snapshot /dev/xvda /dev/xvdc2 N 16" + else + new_table="0 $new_size linear /dev/xvda 0" + fi + dmsetup load dmroot --table "$new_table" + dmsetup resume dmroot fi - dmsetup load dmroot --table "$new_table" - dmsetup resume dmroot resize2fs /dev/mapper/dmroot ;; *)