aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README1
-rw-r--r--src/firejail/x11.c333
2 files changed, 177 insertions, 157 deletions
diff --git a/README b/README
index 08617425f..a01e5fa86 100644
--- a/README
+++ b/README
@@ -109,6 +109,7 @@ Zack Weinberg (https://github.com/zackw)
109 - rework xpra and xephyr detection 109 - rework xpra and xephyr detection
110 - rework abstract X11 socket detection 110 - rework abstract X11 socket detection
111 - rework X11 display number assignment 111 - rework X11 display number assignment
112 - rework X11 xorg processing
112Igor Bukanov (https://github.com/ibukanov) 113Igor Bukanov (https://github.com/ibukanov)
113 - found/fiixed privilege escalation in --hosts-file option 114 - found/fiixed privilege escalation in --hosts-file option
114Cat (https://github.com/ecat3) 115Cat (https://github.com/ecat3)
diff --git a/src/firejail/x11.c b/src/firejail/x11.c
index a415929b1..bde33821d 100644
--- a/src/firejail/x11.c
+++ b/src/firejail/x11.c
@@ -196,101 +196,9 @@ static int random_display_number(void) {
196 } 196 }
197 return display; 197 return display;
198} 198}
199
200
201
202#if 0
203static int random_display_number(void) {
204 int i;
205 int found = 1;
206 int display;
207 for (i = 0; i < 100; i++) {
208 display = rand() % 1024;
209 if (display < 10)
210 continue;
211 char *fname;
212 if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1)
213 errExit("asprintf");
214 struct stat s;
215 if (stat(fname, &s) == -1) {
216 found = 1;
217 break;
218 }
219 }
220 if (!found) {
221 fprintf(stderr, "Error: cannot pick up a random X11 display number, exiting...\n");
222 exit(1);
223 }
224
225 return display;
226}
227#endif 199#endif
228#endif
229
230 200
231 201
232void fs_x11(void) {
233#ifdef HAVE_X11
234 int display = x11_display();
235 if (display <= 0)
236 return;
237
238 char *x11file;
239 if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1)
240 errExit("asprintf");
241 struct stat x11stat;
242 if (stat(x11file, &x11stat) == -1 || !S_ISSOCK(x11stat.st_mode)) {
243 free(x11file);
244 return;
245 }
246
247 if (arg_debug || arg_debug_whitelists)
248 fprintf(stderr, "Masking all X11 sockets except %s\n", x11file);
249
250 // Move the real /tmp/.X11-unix to a scratch location
251 // so we can still access x11file after we mount a
252 // tmpfs over /tmp/.X11-unix.
253 int rv = mkdir(RUN_WHITELIST_X11_DIR, 0700);
254 if (rv == -1)
255 errExit("mkdir");
256 if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 0700))
257 errExit("set_perms");
258
259 if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, 0, MS_BIND|MS_REC, 0) < 0)
260 errExit("mount bind");
261
262 // This directory must be mode 1777, or Xlib will barf.
263 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs",
264 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC,
265 "mode=1777,uid=0,gid=0") < 0)
266 errExit("mounting tmpfs on /tmp/.X11-unix");
267 fs_logger("tmpfs /tmp/.X11-unix");
268
269 // create an empty file which will have the desired socket bind-mounted over it
270 int fd = open(x11file, O_RDWR|O_CREAT|O_EXCL, x11stat.st_mode & ~S_IFMT);
271 if (fd < 0)
272 errExit(x11file);
273 if (fchown(fd, x11stat.st_uid, x11stat.st_gid))
274 errExit("fchown");
275 close(fd);
276
277 // do the mount
278 char *wx11file;
279 if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1)
280 errExit("asprintf");
281 if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0)
282 errExit("mount bind");
283 fs_logger2("whitelist", x11file);
284
285 free(x11file);
286 free(wx11file);
287
288 // block access to RUN_WHITELIST_X11_DIR
289 if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, 0, MS_BIND, 0) < 0)
290 errExit("mount");
291 fs_logger2("blacklist", RUN_WHITELIST_X11_DIR);
292#endif
293}
294 202
295 203
296#ifdef HAVE_X11 204#ifdef HAVE_X11
@@ -720,58 +628,39 @@ void x11_start(int argc, char **argv) {
720 628
721#endif 629#endif
722 630
723void x11_block(void) { 631// Porting notes:
632//
633// 1. merge #1100 from zackw:
634// Attempting to run xauth -f directly on a file in /run/firejail/mnt/ directory fails on Debian 8
635// with this message:
636// xauth: timeout in locking authority file /run/firejail/mnt/sec.Xauthority-Qt5Mu4
637// Failed to create untrusted X cookie: xauth: exit 1
638// For this reason we run xauth on a file in a tmpfs filesystem mounted on /tmp. This was
639// a partial merge.
640//
641// 2. Since we cannot deal with the TOCTOU condition when mounting .Xauthority in user home
642// directory, we need to make sure /usr/bin/xauth executable is the real thing, and not
643// something picked up on $PATH.
644//
645void x11_xorg(void) {
724#ifdef HAVE_X11 646#ifdef HAVE_X11
725 mask_x11_abstract_socket = 1;
726 647
727 // check abstract socket presence and network namespace options 648 // check xauth utility is present in the system
728 if ((!arg_nonetwork && !cfg.bridge0.configured && !cfg.interface0.configured) 649 struct stat s;
729 && x11_abstract_sockets_present()) { 650 if (stat("/usr/bin/xauth", &s) == -1) {
730 fprintf(stderr, "ERROR: --x11=none specified, but abstract X11 socket still accessible.\n" 651 fprintf(stderr, "Error: xauth utility not found in PATH. Please install it:\n"
731 "Additional setup required. To block abstract X11 socket you can either:\n" 652 " Debian/Ubuntu/Mint: sudo apt-get install xauth\n");
732 " * use network namespace in firejail (--net=none, --net=...)\n"
733 " * add \"-nolisten local\" to xserver options\n"
734 " (eg. to your display manager config, or /etc/X11/xinit/xserverrc)\n");
735 exit(1); 653 exit(1);
736 } 654 }
737 655 if (s.st_uid != 0 && s.st_gid != 0) {
738 // blacklist sockets 656 fprintf(stderr, "Error: invalid /usr/bin/xauth executable\n");
739 profile_check_line("blacklist /tmp/.X11-unix", 0, NULL); 657 exit(1);
740 profile_add(strdup("blacklist /tmp/.X11-unix"));
741
742 // blacklist .Xauthority
743 profile_check_line("blacklist ${HOME}/.Xauthority", 0, NULL);
744 profile_add(strdup("blacklist ${HOME}/.Xauthority"));
745 char *xauthority = getenv("XAUTHORITY");
746 if (xauthority) {
747 char *line;
748 if (asprintf(&line, "blacklist %s", xauthority) == -1)
749 errExit("asprintf");
750 profile_check_line(line, 0, NULL);
751 profile_add(line);
752 }
753
754 // clear environment
755 env_store("DISPLAY", RMENV);
756 env_store("XAUTHORITY", RMENV);
757#endif
758}
759
760void x11_xorg(void) {
761#ifdef HAVE_X11
762 // destination - create an empty ~/.Xauthotrity file if it doesn't exist already, and use it as a mount point
763 char *dest;
764 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1)
765 errExit("asprintf");
766 struct stat s;
767 if (stat(dest, &s) == -1) {
768 // create an .Xauthority file
769 touch_file_as_user(dest, getuid(), getgid(), 0600);
770 } 658 }
771 659
772 // check xauth utility is present in the system 660 // get DISPLAY env
773 if (stat("/usr/bin/xauth", &s) == -1) { 661 char *display = getenv("DISPLAY");
774 fprintf(stderr, "Error: cannot find /usr/bin/xauth executable\n"); 662 if (!display) {
663 fputs("Error: --x11=xorg requires an 'outer' X11 server to use.\n", stderr);
775 exit(1); 664 exit(1);
776 } 665 }
777 666
@@ -779,7 +668,9 @@ void x11_xorg(void) {
779 if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) 668 if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
780 errExit("mounting /tmp"); 669 errExit("mounting /tmp");
781 670
782 // create a temporary .Xauthority file 671 // create the temporary .Xauthority file
672 if (arg_debug)
673 printf("Generating a new .Xauthority file\n");
783 char tmpfname[] = "/tmp/.tmpXauth-XXXXXX"; 674 char tmpfname[] = "/tmp/.tmpXauth-XXXXXX";
784 int fd = mkstemp(tmpfname); 675 int fd = mkstemp(tmpfname);
785 if (fd == -1) { 676 if (fd == -1) {
@@ -794,38 +685,48 @@ void x11_xorg(void) {
794 if (child < 0) 685 if (child < 0)
795 errExit("fork"); 686 errExit("fork");
796 if (child == 0) { 687 if (child == 0) {
797 // generate the new .Xauthority file using xauth utility
798 if (arg_debug)
799 printf("Generating a new .Xauthority file\n");
800 drop_privs(1); 688 drop_privs(1);
801
802 char *display = getenv("DISPLAY");
803 if (!display)
804 display = ":0.0";
805
806 clearenv(); 689 clearenv();
807 execlp("/usr/bin/xauth", "/usr/bin/xauth", "-f", tmpfname,
808 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL);
809
810#ifdef HAVE_GCOV 690#ifdef HAVE_GCOV
811 __gcov_flush(); 691 __gcov_flush();
812#endif 692#endif
813 _exit(0); 693 execlp("/usr/bin/xauth", "/usr/bin/xauth", "-f", tmpfname,
694 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL);
695
696 _exit(127);
697 }
698
699 // wait for the xauth process to finish
700 int status;
701 if (waitpid(child, &status, 0) != child)
702 errExit("waitpid");
703 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
704 /* success */
705 } else if (WIFEXITED(status)) {
706 fprintf(stderr, "Failed to create untrusted X cookie: xauth: exit %d\n",
707 WEXITSTATUS(status));
708 exit(1);
709 } else if (WIFSIGNALED(status)) {
710 fprintf(stderr, "Failed to create untrusted X cookie: xauth: %s\n",
711 strsignal(WTERMSIG(status)));
712 exit(1);
713 } else {
714 fprintf(stderr, "Failed to create untrusted X cookie: "
715 "xauth: un-decodable exit status %04x\n", status);
716 exit(1);
814 } 717 }
815 718
816 // wait for the child to finish 719 // ensure the file has the correct permissions and move it
817 waitpid(child, NULL, 0); 720 // into the correct location.
818
819 // check the file was created and set mode and ownership
820 if (stat(tmpfname, &s) == -1) { 721 if (stat(tmpfname, &s) == -1) {
821 fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); 722 fprintf(stderr, "Error: .Xauthority file was mpt created\n");
822 exit(1); 723 exit(1);
823 } 724 }
824 if (set_perms(tmpfname, getuid(), getgid(), 0600)) 725 if (set_perms(tmpfname, getuid(), getgid(), 0600))
825 errExit("set_perms"); 726 errExit("set_perms");
826 727
827 // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted 728 // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted
828 // automatically when the sandbox is closed 729 // automatically when the sandbox is closed (rename doesn't work)
829 if (copy_file(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) { // root needed 730 if (copy_file(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) { // root needed
830 fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); 731 fprintf(stderr, "Error: cannot create the new .Xauthority file\n");
831 exit(1); 732 exit(1);
@@ -834,12 +735,29 @@ void x11_xorg(void) {
834 errExit("set_perms"); 735 errExit("set_perms");
835 /* coverity[toctou] */ 736 /* coverity[toctou] */
836 unlink(tmpfname); 737 unlink(tmpfname);
738 umount("/tmp");
837 739
740
741 // Ensure there is already a file in the usual location, so that bind-mount below will work.
742 // todo: fix TOCTOU races, currently managed by imposing /usr/bin/xauth as executable
743 char *dest;
744 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1)
745 errExit("asprintf");
746 if (stat(dest, &s) == -1) {
747 // create an .Xauthority file
748 touch_file_as_user(dest, getuid(), getgid(), 0600);
749 }
750 if (is_link(dest)) {
751 fprintf(stderr, "Error: .Xauthority is a symbolic link\n");
752 exit(1);
753 }
754
838 // mount 755 // mount
839 if (mount(RUN_XAUTHORITY_SEC_FILE, dest, "none", MS_BIND, "mode=0600") == -1) { 756 if (mount(RUN_XAUTHORITY_SEC_FILE, dest, "none", MS_BIND, "mode=0600") == -1) {
840 fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); 757 fprintf(stderr, "Error: cannot mount the new .Xauthority file\n");
841 exit(1); 758 exit(1);
842 } 759 }
760 // just in case...
843 if (set_perms(dest, getuid(), getgid(), 0600)) 761 if (set_perms(dest, getuid(), getgid(), 0600))
844 errExit("set_perms"); 762 errExit("set_perms");
845 free(dest); 763 free(dest);
@@ -848,3 +766,104 @@ void x11_xorg(void) {
848 umount("/tmp"); 766 umount("/tmp");
849#endif 767#endif
850} 768}
769
770void fs_x11(void) {
771#ifdef HAVE_X11
772 int display = x11_display();
773 if (display <= 0)
774 return;
775
776 char *x11file;
777 if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1)
778 errExit("asprintf");
779 struct stat x11stat;
780 if (stat(x11file, &x11stat) == -1 || !S_ISSOCK(x11stat.st_mode)) {
781 free(x11file);
782 return;
783 }
784
785 if (arg_debug || arg_debug_whitelists)
786 fprintf(stderr, "Masking all X11 sockets except %s\n", x11file);
787
788 // Move the real /tmp/.X11-unix to a scratch location
789 // so we can still access x11file after we mount a
790 // tmpfs over /tmp/.X11-unix.
791 int rv = mkdir(RUN_WHITELIST_X11_DIR, 0700);
792 if (rv == -1)
793 errExit("mkdir");
794 if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 0700))
795 errExit("set_perms");
796
797 if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, 0, MS_BIND|MS_REC, 0) < 0)
798 errExit("mount bind");
799
800 // This directory must be mode 1777, or Xlib will barf.
801 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs",
802 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC,
803 "mode=1777,uid=0,gid=0") < 0)
804 errExit("mounting tmpfs on /tmp/.X11-unix");
805 fs_logger("tmpfs /tmp/.X11-unix");
806
807 // create an empty file which will have the desired socket bind-mounted over it
808 int fd = open(x11file, O_RDWR|O_CREAT|O_EXCL, x11stat.st_mode & ~S_IFMT);
809 if (fd < 0)
810 errExit(x11file);
811 if (fchown(fd, x11stat.st_uid, x11stat.st_gid))
812 errExit("fchown");
813 close(fd);
814
815 // do the mount
816 char *wx11file;
817 if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1)
818 errExit("asprintf");
819 if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0)
820 errExit("mount bind");
821 fs_logger2("whitelist", x11file);
822
823 free(x11file);
824 free(wx11file);
825
826 // block access to RUN_WHITELIST_X11_DIR
827 if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, 0, MS_BIND, 0) < 0)
828 errExit("mount");
829 fs_logger2("blacklist", RUN_WHITELIST_X11_DIR);
830#endif
831}
832
833void x11_block(void) {
834#ifdef HAVE_X11
835 mask_x11_abstract_socket = 1;
836
837 // check abstract socket presence and network namespace options
838 if ((!arg_nonetwork && !cfg.bridge0.configured && !cfg.interface0.configured)
839 && x11_abstract_sockets_present()) {
840 fprintf(stderr, "ERROR: --x11=none specified, but abstract X11 socket still accessible.\n"
841 "Additional setup required. To block abstract X11 socket you can either:\n"
842 " * use network namespace in firejail (--net=none, --net=...)\n"
843 " * add \"-nolisten local\" to xserver options\n"
844 " (eg. to your display manager config, or /etc/X11/xinit/xserverrc)\n");
845 exit(1);
846 }
847
848 // blacklist sockets
849 profile_check_line("blacklist /tmp/.X11-unix", 0, NULL);
850 profile_add(strdup("blacklist /tmp/.X11-unix"));
851
852 // blacklist .Xauthority
853 profile_check_line("blacklist ${HOME}/.Xauthority", 0, NULL);
854 profile_add(strdup("blacklist ${HOME}/.Xauthority"));
855 char *xauthority = getenv("XAUTHORITY");
856 if (xauthority) {
857 char *line;
858 if (asprintf(&line, "blacklist %s", xauthority) == -1)
859 errExit("asprintf");
860 profile_check_line(line, 0, NULL);
861 profile_add(line);
862 }
863
864 // clear environment
865 env_store("DISPLAY", RMENV);
866 env_store("XAUTHORITY", RMENV);
867#endif
868}
869