qfile-unpacker.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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
  73. gui_fatal("Invalid option %s", argv[i]);
  74. }
  75. if (chdir(incoming_dir))
  76. gui_fatal("Error chdir to %s", incoming_dir);
  77. if (mount(".", ".", NULL, MS_BIND | MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0)
  78. gui_fatal("Failed to mount a directory %s", incoming_dir);
  79. /* parse the input in unprivileged child process, parent will hold root
  80. * access to unmount incoming dir */
  81. switch (pid=fork()) {
  82. case -1:
  83. gui_fatal("Failed to create new process");
  84. case 0:
  85. if (asprintf(&procdir_path, "/proc/%d/fd", getpid()) < 0) {
  86. gui_fatal("Error allocating memory");
  87. }
  88. procfs_fd = open(procdir_path, O_DIRECTORY | O_RDONLY);
  89. if (procfs_fd < 0)
  90. perror("Failed to open /proc");
  91. else
  92. set_procfs_fd(procfs_fd);
  93. free(procdir_path);
  94. if (chroot("."))
  95. gui_fatal("Error chroot to %s", incoming_dir);
  96. if (setuid(uid) < 0) {
  97. /* no kdialog inside chroot */
  98. perror("setuid");
  99. exit(1);
  100. }
  101. return do_unpack();
  102. }
  103. if (waitpid(pid, &ret, 0) < 0) {
  104. gui_fatal("Failed to wait for child process");
  105. }
  106. if (umount2(".", MNT_DETACH) < 0)
  107. gui_fatal("Cannot umount incoming directory");
  108. if (!WIFEXITED(ret)) {
  109. gui_fatal("Child process exited abnormally");
  110. }
  111. return WEXITSTATUS(ret);
  112. }