qfile-unpacker.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #define _GNU_SOURCE
  2. #include <grp.h>
  3. #include <unistd.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <pwd.h>
  7. #include <sys/stat.h>
  8. #include <sys/mount.h>
  9. #include <sys/wait.h>
  10. #include <fcntl.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include <sys/fsuid.h>
  14. #include <gui-fatal.h>
  15. #include <errno.h>
  16. #include <libqubes-rpc-filecopy.h>
  17. #define INCOMING_DIR_NAME "QubesIncoming"
  18. char *prepare_creds_return_dir(int uid)
  19. {
  20. const struct passwd *pwd;
  21. pwd = getpwuid(uid);
  22. if (!pwd) {
  23. perror("getpwuid");
  24. exit(1);
  25. }
  26. setenv("HOME", pwd->pw_dir, 1);
  27. setenv("USER", pwd->pw_name, 1);
  28. if (setgid(pwd->pw_gid) < 0)
  29. gui_fatal("Error setting group permissions");
  30. if (initgroups(pwd->pw_name, pwd->pw_gid) < 0)
  31. gui_fatal("Error initializing groups");
  32. if (setfsuid(pwd->pw_uid) < 0)
  33. gui_fatal("Error setting filesystem level permissions");
  34. return pwd->pw_dir;
  35. }
  36. int main(int argc, char ** argv)
  37. {
  38. char *home_dir;
  39. char *incoming_dir_root;
  40. char *incoming_dir;
  41. int uid, ret;
  42. pid_t pid;
  43. const char *remote_domain;
  44. char *procdir_path;
  45. int procfs_fd;
  46. int i;
  47. if (argc >= 3) {
  48. errno = 0;
  49. uid = strtol(argv[1], NULL, 10);
  50. if (errno)
  51. gui_fatal("Invalid user ID argument");
  52. home_dir = prepare_creds_return_dir(uid);
  53. incoming_dir = argv[2];
  54. } else {
  55. uid = getuid();
  56. home_dir = prepare_creds_return_dir(uid);
  57. remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
  58. if (!remote_domain) {
  59. gui_fatal("Cannot get remote domain name");
  60. }
  61. if (asprintf(&incoming_dir_root, "%s/%s", home_dir, INCOMING_DIR_NAME) < 0) {
  62. gui_fatal("Error allocating memory");
  63. }
  64. mkdir(incoming_dir_root, 0700);
  65. if (asprintf(&incoming_dir, "%s/%s", incoming_dir_root, remote_domain) < 0)
  66. gui_fatal("Error allocating memory");
  67. mkdir(incoming_dir, 0700);
  68. }
  69. for (i = 3; i < argc; i++) {
  70. if (strcmp(argv[i], "-v") == 0)
  71. set_verbose(1);
  72. else if (strcmp(argv[i], "-w") == 0)
  73. if (i+1 < argc && argv[i+1][0] != '-') {
  74. set_wait_for_space(atoi(argv[i+1]));
  75. i++;
  76. } else
  77. set_wait_for_space(1);
  78. else
  79. gui_fatal("Invalid option %s", argv[i]);
  80. }
  81. if (chdir(incoming_dir))
  82. gui_fatal("Error chdir to %s", incoming_dir);
  83. if (mount(".", ".", NULL, MS_BIND | MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0)
  84. gui_fatal("Failed to mount a directory %s", incoming_dir);
  85. /* parse the input in unprivileged child process, parent will hold root
  86. * access to unmount incoming dir */
  87. switch (pid=fork()) {
  88. case -1:
  89. gui_fatal("Failed to create new process");
  90. case 0:
  91. if (asprintf(&procdir_path, "/proc/%d/fd", getpid()) < 0) {
  92. gui_fatal("Error allocating memory");
  93. }
  94. procfs_fd = open(procdir_path, O_DIRECTORY | O_RDONLY);
  95. if (procfs_fd < 0)
  96. perror("Failed to open /proc");
  97. else
  98. set_procfs_fd(procfs_fd);
  99. free(procdir_path);
  100. if (chroot("."))
  101. gui_fatal("Error chroot to %s", incoming_dir);
  102. if (setuid(uid) < 0) {
  103. /* no kdialog inside chroot */
  104. perror("setuid");
  105. exit(1);
  106. }
  107. return do_unpack();
  108. }
  109. if (waitpid(pid, &ret, 0) < 0) {
  110. gui_fatal("Failed to wait for child process");
  111. }
  112. if (umount2(".", MNT_DETACH) < 0)
  113. gui_fatal("Cannot umount incoming directory");
  114. if (!WIFEXITED(ret)) {
  115. gui_fatal("Child process exited abnormally");
  116. }
  117. return WEXITSTATUS(ret);
  118. }