Skip to content

Commit

Permalink
qfile-unpacker: Add flags to allow unsafe characters and symlinks
Browse files Browse the repository at this point in the history
  • Loading branch information
DemiMarie committed May 14, 2024
1 parent b1c9acb commit 6a651fa
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 21 deletions.
1 change: 1 addition & 0 deletions qubes-rpc/qfile-agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <gui-fatal.h>
#include <libqubes-rpc-filecopy.h>

Expand Down
132 changes: 111 additions & 21 deletions qubes-rpc/qfile-unpacker.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@
#include <string.h>
#include <unistd.h>
#include <sys/fsuid.h>
#include <gui-fatal.h>
#include <errno.h>
#include <inttypes.h>
#include <assert.h>
#include <limits.h>
#include <getopt.h>
#include <err.h>

#include <gui-fatal.h>
#include <libqubes-rpc-filecopy.h>

#define INCOMING_DIR_NAME "QubesIncoming"
Expand All @@ -40,6 +45,41 @@ static char *prepare_creds_return_dir(uid_t uid)
return pwd->pw_dir;
}

static void set_wait_for_space_str(const char *str)
{
if (strcmp(str, "0") == 0) {
set_wait_for_space(0);
return;
}
if (str[0] >= '1' && str[0] <= '9') {
errno = 0;
char *endp;
long res = strtol(str, &endp, 10);
if (errno == 0 && *endp == '\0' && res > 0 && res <= INT_MAX) {
set_wait_for_space((int)res);
return;
}
}
errx(1, "Space amount %s is invalid or exceeds %d bytes", str, INT_MAX);
}

enum {
opt_allow_unsafe_characters = 256,
opt_allow_unsafe_symlinks,
opt_no_allow_unsafe_characters,
opt_no_allow_unsafe_symlinks,
};

const struct option opts[] = {
{ "no-allow-unsafe-characters", no_argument, NULL, opt_no_allow_unsafe_characters },
{ "allow-unsafe-characters", no_argument, NULL, opt_allow_unsafe_characters },
{ "no-allow-unsafe-symlinks", no_argument, NULL, opt_no_allow_unsafe_symlinks },
{ "allow-unsafe-symlinks", no_argument, NULL, opt_allow_unsafe_symlinks },
{ "verbose", no_argument, NULL, 'v' },
{ "wait-for-space", required_argument, NULL, 'w' },
{ NULL, 0, NULL, 0 },
};

int main(int argc, char ** argv)
{
char *home_dir;
Expand All @@ -51,24 +91,87 @@ int main(int argc, char ** argv)
char *procdir_path;
int procfs_fd;
int i, ret;

if (argc >= 3) {
errno = 0;
int flags = COPY_ALLOW_SYMLINKS | COPY_ALLOW_DIRECTORIES;
if (argc < 1)
errx(EXIT_FAILURE, "NULL argv[0] passed to execve()");
if (argc >= 3 && argv[1][0] >= '0' && argv[1][0] <= '9') {
// Legacy case: parse options by hand
char buf[21];
int r = snprintf(buf, sizeof buf, "%" PRIu64, (uint64_t)uid);
if (r < 1 || r >= (int)sizeof(buf))
abort(); // snprintf() failed
if (strcmp(buf, argv[1]) != 0)
gui_fatal("Called with wrong user ID as argument");
home_dir = prepare_creds_return_dir(uid);
incoming_dir = argv[2];

for (i = 3; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0)
set_verbose(1);
else if (strcmp(argv[i], "-w") == 0) {
const char *next = argv[i + 1];
if (next != NULL && next[0] != '-') {
set_wait_for_space_str(next);
i++;
} else {
set_wait_for_space(1);
}
} else {
gui_fatal("Invalid option %s", argv[i]);
}
}
} else {
home_dir = prepare_creds_return_dir(uid);
incoming_dir = NULL;
// Modern case: use getopt(3)
for (;;) {
if (optind < 1 || optind > argc) {
// FIXME: is this actually impossible?
assert(!"invalid optind() value?");
abort();
}
int longindex = -1;
const char *const last = argv[optind];
int opt = getopt_long(argc, argv, "+vw:", opts, &longindex);
if (opt == -1) {
if (argc - optind > 1)
errx(1, "Too many non-option arguments (expected 1)");
incoming_dir = argv[optind];
break;
}
if (opt == '?' || opt == ':')
return EXIT_FAILURE;
if (longindex != -1) {
const char *expected = opts[longindex].name;
if (strncmp(expected, last + 2, strlen(expected)) != 0)
errx(1, "Option %s must be passed as --%s", last, expected);
}
switch (opt) {
case 'v':
set_verbose(1);
break;
case opt_allow_unsafe_characters:
flags |= COPY_ALLOW_UNSAFE_CHARACTERS;
break;
case opt_allow_unsafe_symlinks:
flags |= COPY_ALLOW_UNSAFE_SYMLINKS;
break;
case opt_no_allow_unsafe_characters:
flags &= ~COPY_ALLOW_UNSAFE_CHARACTERS;
break;
case opt_no_allow_unsafe_symlinks:
flags &= ~COPY_ALLOW_UNSAFE_SYMLINKS;
break;
case 'w':
set_wait_for_space_str(optarg);
break;
}
}
}
home_dir = prepare_creds_return_dir(uid);
if (incoming_dir == NULL) {
remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
if (!remote_domain) {
gui_fatal("Cannot get remote domain name");
}

if (asprintf(&incoming_dir_root, "%s/%s", home_dir, INCOMING_DIR_NAME) < 0) {
gui_fatal("Error allocating memory");
}
Expand All @@ -80,19 +183,6 @@ int main(int argc, char ** argv)
mkdir(incoming_dir, 0700);
}

for (i = 3; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0)
set_verbose(1);
else if (strcmp(argv[i], "-w") == 0)
if (i+1 < argc && argv[i+1][0] != '-') {
set_wait_for_space(atoi(argv[i+1]));
i++;
} else
set_wait_for_space(1);
else
gui_fatal("Invalid option %s", argv[i]);
}

if (chdir(incoming_dir))
gui_fatal("Error chdir to %s", incoming_dir);

Expand Down Expand Up @@ -124,7 +214,7 @@ int main(int argc, char ** argv)
perror("setuid");
exit(1);
}
return do_unpack();
return do_unpack_ext(flags);
}
if (waitpid(pid, &ret, 0) < 0) {
gui_fatal("Failed to wait for child process");
Expand Down

0 comments on commit 6a651fa

Please sign in to comment.