summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fcopy/main.c2
-rw-r--r--src/firecfg/firecfg.config2
-rw-r--r--src/firejail/bandwidth.c9
-rw-r--r--src/firejail/firejail.h4
-rw-r--r--src/firejail/fs.c80
-rw-r--r--src/firejail/fs_home.c112
-rw-r--r--src/firejail/ls.c8
-rw-r--r--src/firejail/main.c29
-rw-r--r--src/firejail/preproc.c8
-rw-r--r--src/firejail/pulseaudio.c63
-rw-r--r--src/firejail/sandbox.c11
-rw-r--r--src/firejail/util.c50
-rw-r--r--src/man/firejail.txt4
13 files changed, 265 insertions, 117 deletions
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
index b1e2813db..a4f5ace11 100644
--- a/src/fcopy/main.c
+++ b/src/fcopy/main.c
@@ -41,7 +41,7 @@ static void copy_file(const char *srcname, const char *destname, mode_t mode, ui
41 // open source 41 // open source
42 int src = open(srcname, O_RDONLY); 42 int src = open(srcname, O_RDONLY);
43 if (src < 0) { 43 if (src < 0) {
44 fprintf(stderr, "Warning: cannot open %s, file not copied\n", srcname); 44 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", srcname);
45 return; 45 return;
46 } 46 }
47 47
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config
index fe65a5077..4e4e5488a 100644
--- a/src/firecfg/firecfg.config
+++ b/src/firecfg/firecfg.config
@@ -184,8 +184,6 @@ eog
184# other 184# other
185atom 185atom
186atom-beta 186atom-beta
187gpa
188gpg
189ranger 187ranger
190keepass 188keepass
191keepass2 189keepass2
diff --git a/src/firejail/bandwidth.c b/src/firejail/bandwidth.c
index 5e9002f22..84c9dc53a 100644
--- a/src/firejail/bandwidth.c
+++ b/src/firejail/bandwidth.c
@@ -435,15 +435,8 @@ void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, in
435 if (setregid(0, 0)) 435 if (setregid(0, 0))
436 errExit("setregid"); 436 errExit("setregid");
437 437
438 if (!cfg.shell)
439 cfg.shell = guess_shell();
440 if (!cfg.shell) {
441 fprintf(stderr, "Error: no POSIX shell found, please use --shell command line option\n");
442 exit(1);
443 }
444
445 char *arg[4]; 438 char *arg[4];
446 arg[0] = cfg.shell; 439 arg[0] = "/bin/sh";
447 arg[1] = "-c"; 440 arg[1] = "-c";
448 arg[2] = cmd; 441 arg[2] = cmd;
449 arg[3] = NULL; 442 arg[3] = NULL;
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 36cf47435..586cfd65e 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -403,7 +403,7 @@ char *fs_check_overlay_dir(const char *subdirname, int allow_reuse);
403void fs_overlayfs(void); 403void fs_overlayfs(void);
404// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf 404// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
405void fs_chroot(const char *rootdir); 405void fs_chroot(const char *rootdir);
406int fs_check_chroot_dir(const char *rootdir); 406void fs_check_chroot_dir(const char *rootdir);
407 407
408// profile.c 408// profile.c
409// find and read the profile specified by name from dir directory 409// find and read the profile specified by name from dir directory
@@ -450,6 +450,8 @@ void logmsg(const char *msg);
450void logargs(int argc, char **argv) ; 450void logargs(int argc, char **argv) ;
451void logerr(const char *msg); 451void logerr(const char *msg);
452int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 452int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
453void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
454void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode);
453int is_dir(const char *fname); 455int is_dir(const char *fname);
454int is_link(const char *fname); 456int is_link(const char *fname);
455char *line_remove_spaces(const char *buf); 457char *line_remove_spaces(const char *buf);
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index e2fc09533..d7764accd 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -994,20 +994,25 @@ void fs_overlayfs(void) {
994 994
995#ifdef HAVE_CHROOT 995#ifdef HAVE_CHROOT
996// return 1 if error 996// return 1 if error
997int fs_check_chroot_dir(const char *rootdir) { 997void fs_check_chroot_dir(const char *rootdir) {
998 EUID_ASSERT(); 998 EUID_ASSERT();
999 assert(rootdir); 999 assert(rootdir);
1000 struct stat s; 1000 struct stat s;
1001 char *name; 1001 char *name;
1002 1002
1003 if (strcmp(rootdir, "/tmp") == 0 || strcmp(rootdir, "/var/tmp") == 0) {
1004 fprintf(stderr, "Error: invalid chroot directory\n");
1005 exit(1);
1006 }
1007
1003 // rootdir has to be owned by root 1008 // rootdir has to be owned by root
1004 if (stat(rootdir, &s) != 0) { 1009 if (stat(rootdir, &s) != 0) {
1005 fprintf(stderr, "Error: cannot find chroot directory\n"); 1010 fprintf(stderr, "Error: cannot find chroot directory\n");
1006 return 1; 1011 exit(1);
1007 } 1012 }
1008 if (s.st_uid != 0) { 1013 if (s.st_uid != 0) {
1009 fprintf(stderr, "Error: chroot directory should be owned by root\n"); 1014 fprintf(stderr, "Error: chroot directory should be owned by root\n");
1010 return 1; 1015 exit(1);
1011 } 1016 }
1012 1017
1013 // check /dev 1018 // check /dev
@@ -1015,7 +1020,11 @@ int fs_check_chroot_dir(const char *rootdir) {
1015 errExit("asprintf"); 1020 errExit("asprintf");
1016 if (stat(name, &s) == -1) { 1021 if (stat(name, &s) == -1) {
1017 fprintf(stderr, "Error: cannot find /dev in chroot directory\n"); 1022 fprintf(stderr, "Error: cannot find /dev in chroot directory\n");
1018 return 1; 1023 exit(1);
1024 }
1025 if (s.st_uid != 0) {
1026 fprintf(stderr, "Error: chroot /dev directory should be owned by root\n");
1027 exit(1);
1019 } 1028 }
1020 free(name); 1029 free(name);
1021 1030
@@ -1024,7 +1033,11 @@ int fs_check_chroot_dir(const char *rootdir) {
1024 errExit("asprintf"); 1033 errExit("asprintf");
1025 if (stat(name, &s) == -1) { 1034 if (stat(name, &s) == -1) {
1026 fprintf(stderr, "Error: cannot find /var/tmp in chroot directory\n"); 1035 fprintf(stderr, "Error: cannot find /var/tmp in chroot directory\n");
1027 return 1; 1036 exit(1);
1037 }
1038 if (s.st_uid != 0) {
1039 fprintf(stderr, "Error: chroot /var/tmp directory should be owned by root\n");
1040 exit(1);
1028 } 1041 }
1029 free(name); 1042 free(name);
1030 1043
@@ -1033,7 +1046,11 @@ int fs_check_chroot_dir(const char *rootdir) {
1033 errExit("asprintf"); 1046 errExit("asprintf");
1034 if (stat(name, &s) == -1) { 1047 if (stat(name, &s) == -1) {
1035 fprintf(stderr, "Error: cannot find /proc in chroot directory\n"); 1048 fprintf(stderr, "Error: cannot find /proc in chroot directory\n");
1036 return 1; 1049 exit(1);
1050 }
1051 if (s.st_uid != 0) {
1052 fprintf(stderr, "Error: chroot /proc directory should be owned by root\n");
1053 exit(1);
1037 } 1054 }
1038 free(name); 1055 free(name);
1039 1056
@@ -1042,18 +1059,41 @@ int fs_check_chroot_dir(const char *rootdir) {
1042 errExit("asprintf"); 1059 errExit("asprintf");
1043 if (stat(name, &s) == -1) { 1060 if (stat(name, &s) == -1) {
1044 fprintf(stderr, "Error: cannot find /tmp in chroot directory\n"); 1061 fprintf(stderr, "Error: cannot find /tmp in chroot directory\n");
1045 return 1; 1062 exit(1);
1063 }
1064 if (s.st_uid != 0) {
1065 fprintf(stderr, "Error: chroot /tmp directory should be owned by root\n");
1066 exit(1);
1046 } 1067 }
1047 free(name); 1068 free(name);
1048 1069
1049 // check /bin/bash 1070 // check /etc
1050// if (asprintf(&name, "%s/bin/bash", rootdir) == -1) 1071 if (asprintf(&name, "%s/etc", rootdir) == -1)
1051// errExit("asprintf"); 1072 errExit("asprintf");
1052// if (stat(name, &s) == -1) { 1073 if (stat(name, &s) == -1) {
1053// fprintf(stderr, "Error: cannot find /bin/bash in chroot directory\n"); 1074 fprintf(stderr, "Error: cannot find /etc in chroot directory\n");
1054// return 1; 1075 exit(1);
1055// } 1076 }
1056// free(name); 1077 if (s.st_uid != 0) {
1078 fprintf(stderr, "Error: chroot /etc directory should be owned by root\n");
1079 exit(1);
1080 }
1081 free(name);
1082
1083 // check /etc/resolv.conf
1084 if (asprintf(&name, "%s/etc/resolv.conf", rootdir) == -1)
1085 errExit("asprintf");
1086 if (stat(name, &s) == 0) {
1087 if (s.st_uid != 0) {
1088 fprintf(stderr, "Error: chroot /etc/resolv.conf should be owned by root\n");
1089 exit(1);
1090 }
1091 }
1092 if (is_link(name)) {
1093 fprintf(stderr, "Error: invalid %s file\n", name);
1094 exit(1);
1095 }
1096 free(name);
1057 1097
1058 // check x11 socket directory 1098 // check x11 socket directory
1059 if (getenv("FIREJAIL_X11")) { 1099 if (getenv("FIREJAIL_X11")) {
@@ -1063,12 +1103,14 @@ int fs_check_chroot_dir(const char *rootdir) {
1063 errExit("asprintf"); 1103 errExit("asprintf");
1064 if (stat(name, &s) == -1) { 1104 if (stat(name, &s) == -1) {
1065 fprintf(stderr, "Error: cannot find /tmp/.X11-unix in chroot directory\n"); 1105 fprintf(stderr, "Error: cannot find /tmp/.X11-unix in chroot directory\n");
1066 return 1; 1106 exit(1);
1107 }
1108 if (s.st_uid != 0) {
1109 fprintf(stderr, "Error: chroot /tmp/.X11-unix directory should be owned by root\n");
1110 exit(1);
1067 } 1111 }
1068 free(name); 1112 free(name);
1069 } 1113 }
1070
1071 return 0;
1072} 1114}
1073 1115
1074// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf 1116// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
@@ -1129,7 +1171,7 @@ void fs_chroot(const char *rootdir) {
1129 fprintf(stderr, "Error: invalid %s file\n", fname); 1171 fprintf(stderr, "Error: invalid %s file\n", fname);
1130 exit(1); 1172 exit(1);
1131 } 1173 }
1132 if (copy_file("/etc/resolv.conf", fname, 0, 0, 0644) == -1) 1174 if (copy_file("/etc/resolv.conf", fname, 0, 0, 0644) == -1) // root needed
1133 fprintf(stderr, "Warning: /etc/resolv.conf not initialized\n"); 1175 fprintf(stderr, "Warning: /etc/resolv.conf not initialized\n");
1134 } 1176 }
1135 1177
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 0872bf0d0..8a52314ed 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -42,19 +42,17 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
42 // don't copy it if we already have the file 42 // don't copy it if we already have the file
43 if (stat(fname, &s) == 0) 43 if (stat(fname, &s) == 0)
44 return; 44 return;
45 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat
46 fprintf(stderr, "Error: invalid %s file\n", fname);
47 exit(1);
48 }
45 if (stat("/etc/skel/.zshrc", &s) == 0) { 49 if (stat("/etc/skel/.zshrc", &s) == 0) {
46 if (copy_file("/etc/skel/.zshrc", fname, u, g, 0644) == 0) { 50 copy_file_as_user("/etc/skel/.zshrc", fname, u, g, 0644); // regular user
47 fs_logger("clone /etc/skel/.zshrc"); 51 fs_logger("clone /etc/skel/.zshrc");
48 }
49 } 52 }
50 else { // 53 else {
51 FILE *fp = fopen(fname, "w"); 54 touch_file_as_user(fname, u, g, 0644);
52 if (fp) { 55 fs_logger2("touch", fname);
53 fprintf(fp, "\n");
54 SET_PERMS_STREAM(fp, u, g, S_IRUSR | S_IWUSR);
55 fclose(fp);
56 fs_logger2("touch", fname);
57 }
58 } 56 }
59 free(fname); 57 free(fname);
60 } 58 }
@@ -64,23 +62,21 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
64 if (asprintf(&fname, "%s/.cshrc", homedir) == -1) 62 if (asprintf(&fname, "%s/.cshrc", homedir) == -1)
65 errExit("asprintf"); 63 errExit("asprintf");
66 struct stat s; 64 struct stat s;
65
67 // don't copy it if we already have the file 66 // don't copy it if we already have the file
68 if (stat(fname, &s) == 0) 67 if (stat(fname, &s) == 0)
69 return; 68 return;
69 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat
70 fprintf(stderr, "Error: invalid %s file\n", fname);
71 exit(1);
72 }
70 if (stat("/etc/skel/.cshrc", &s) == 0) { 73 if (stat("/etc/skel/.cshrc", &s) == 0) {
71 if (copy_file("/etc/skel/.cshrc", fname, u, g, 0644) == 0) { 74 copy_file_as_user("/etc/skel/.cshrc", fname, u, g, 0644); // regular user
72 fs_logger("clone /etc/skel/.cshrc"); 75 fs_logger("clone /etc/skel/.cshrc");
73 }
74 } 76 }
75 else { // 77 else {
76 /* coverity[toctou] */ 78 touch_file_as_user(fname, u, g, 0644);
77 FILE *fp = fopen(fname, "w"); 79 fs_logger2("touch", fname);
78 if (fp) {
79 fprintf(fp, "\n");
80 SET_PERMS_STREAM(fp, u, g, S_IRUSR | S_IWUSR);
81 fclose(fp);
82 fs_logger2("touch", fname);
83 }
84 } 80 }
85 free(fname); 81 free(fname);
86 } 82 }
@@ -93,10 +89,13 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
93 // don't copy it if we already have the file 89 // don't copy it if we already have the file
94 if (stat(fname, &s) == 0) 90 if (stat(fname, &s) == 0)
95 return; 91 return;
92 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat
93 fprintf(stderr, "Error: invalid %s file\n", fname);
94 exit(1);
95 }
96 if (stat("/etc/skel/.bashrc", &s) == 0) { 96 if (stat("/etc/skel/.bashrc", &s) == 0) {
97 if (copy_file("/etc/skel/.bashrc", fname, u, g, 0644) == 0) { 97 copy_file_as_user("/etc/skel/.bashrc", fname, u, g, 0644); // regular user
98 fs_logger("clone /etc/skel/.bashrc"); 98 fs_logger("clone /etc/skel/.bashrc");
99 }
100 } 99 }
101 free(fname); 100 free(fname);
102 } 101 }
@@ -106,6 +105,14 @@ static int store_xauthority(void) {
106 // put a copy of .Xauthority in XAUTHORITY_FILE 105 // put a copy of .Xauthority in XAUTHORITY_FILE
107 char *src; 106 char *src;
108 char *dest = RUN_XAUTHORITY_FILE; 107 char *dest = RUN_XAUTHORITY_FILE;
108 // create an empty file as root, and change ownership to user
109 FILE *fp = fopen(dest, "w");
110 if (fp) {
111 fprintf(fp, "\n");
112 SET_PERMS_STREAM(fp, getuid(), getgid(), 0600);
113 fclose(fp);
114 }
115
109 if (asprintf(&src, "%s/.Xauthority", cfg.homedir) == -1) 116 if (asprintf(&src, "%s/.Xauthority", cfg.homedir) == -1)
110 errExit("asprintf"); 117 errExit("asprintf");
111 118
@@ -115,12 +122,9 @@ static int store_xauthority(void) {
115 fprintf(stderr, "Warning: invalid .Xauthority file\n"); 122 fprintf(stderr, "Warning: invalid .Xauthority file\n");
116 return 0; 123 return 0;
117 } 124 }
118 125
119 int rv = copy_file(src, dest, -1, -1, 0600); 126 copy_file_as_user(src, dest, getuid(), getgid(), 0600); // regular user
120 if (rv) { 127 fs_logger2("clone", dest);
121 fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n");
122 return 0;
123 }
124 return 1; // file copied 128 return 1; // file copied
125 } 129 }
126 130
@@ -128,8 +132,17 @@ static int store_xauthority(void) {
128} 132}
129 133
130static int store_asoundrc(void) { 134static int store_asoundrc(void) {
135 // put a copy of .Xauthority in XAUTHORITY_FILE
131 char *src; 136 char *src;
132 char *dest = RUN_ASOUNDRC_FILE; 137 char *dest = RUN_ASOUNDRC_FILE;
138 // create an empty file as root, and change ownership to user
139 FILE *fp = fopen(dest, "w");
140 if (fp) {
141 fprintf(fp, "\n");
142 SET_PERMS_STREAM(fp, getuid(), getgid(), 0644);
143 fclose(fp);
144 }
145
133 if (asprintf(&src, "%s/.asoundrc", cfg.homedir) == -1) 146 if (asprintf(&src, "%s/.asoundrc", cfg.homedir) == -1)
134 errExit("asprintf"); 147 errExit("asprintf");
135 148
@@ -150,11 +163,8 @@ static int store_asoundrc(void) {
150 free(rp); 163 free(rp);
151 } 164 }
152 165
153 int rv = copy_file(src, dest, -1, -1, -0644); 166 copy_file_as_user(src, dest, getuid(), getgid(), 0644); // regular user
154 if (rv) { 167 fs_logger2("clone", dest);
155 fprintf(stderr, "Warning: cannot transfer .asoundrc in private home directory\n");
156 return 0;
157 }
158 return 1; // file copied 168 return 1; // file copied
159 } 169 }
160 170
@@ -167,13 +177,15 @@ static void copy_xauthority(void) {
167 char *dest; 177 char *dest;
168 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) 178 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1)
169 errExit("asprintf"); 179 errExit("asprintf");
170 // copy, set permissions and ownership 180
171 int rv = copy_file(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); 181 // if destination is a symbolic link, exit the sandbox!!!
172 if (rv) 182 if (is_link(dest)) {
173 fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n"); 183 fprintf(stderr, "Error: %s is a symbolic link\n", dest);
174 else { 184 exit(1);
175 fs_logger2("clone", dest);
176 } 185 }
186
187 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user
188 fs_logger2("clone", dest);
177 189
178 // delete the temporary file 190 // delete the temporary file
179 unlink(src); 191 unlink(src);
@@ -185,14 +197,16 @@ static void copy_asoundrc(void) {
185 char *dest; 197 char *dest;
186 if (asprintf(&dest, "%s/.asoundrc", cfg.homedir) == -1) 198 if (asprintf(&dest, "%s/.asoundrc", cfg.homedir) == -1)
187 errExit("asprintf"); 199 errExit("asprintf");
188 // copy, set permissions and ownership 200
189 int rv = copy_file(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); 201 // if destination is a symbolic link, exit the sandbox!!!
190 if (rv) 202 if (is_link(dest)) {
191 fprintf(stderr, "Warning: cannot transfer .asoundrc in private home directory\n"); 203 fprintf(stderr, "Error: %s is a symbolic link\n", dest);
192 else { 204 exit(1);
193 fs_logger2("clone", dest);
194 } 205 }
195 206
207 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user
208 fs_logger2("clone", dest);
209
196 // delete the temporary file 210 // delete the temporary file
197 unlink(src); 211 unlink(src);
198} 212}
diff --git a/src/firejail/ls.c b/src/firejail/ls.c
index 77eb35f97..1af56751a 100644
--- a/src/firejail/ls.c
+++ b/src/firejail/ls.c
@@ -336,7 +336,7 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
336 drop_privs(0); 336 drop_privs(0);
337 337
338 // copy the file 338 // copy the file
339 if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) 339 if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) // already a regular user
340 _exit(1); 340 _exit(1);
341#ifdef HAVE_GCOV 341#ifdef HAVE_GCOV
342 __gcov_flush(); 342 __gcov_flush();
@@ -362,7 +362,7 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
362 drop_privs(0); 362 drop_privs(0);
363 363
364 // copy the file 364 // copy the file
365 if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) 365 if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) // already a regular user
366 _exit(1); 366 _exit(1);
367#ifdef HAVE_GCOV 367#ifdef HAVE_GCOV
368 __gcov_flush(); 368 __gcov_flush();
@@ -411,7 +411,7 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
411 drop_privs(0); 411 drop_privs(0);
412 412
413 // copy the file 413 // copy the file
414 if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) 414 if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) // already a regular user
415 _exit(1); 415 _exit(1);
416#ifdef HAVE_GCOV 416#ifdef HAVE_GCOV
417 __gcov_flush(); 417 __gcov_flush();
@@ -443,7 +443,7 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
443 drop_privs(0); 443 drop_privs(0);
444 444
445 // copy the file 445 // copy the file
446 if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) 446 if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) // already a regular user
447 _exit(1); 447 _exit(1);
448#ifdef HAVE_GCOV 448#ifdef HAVE_GCOV
449 __gcov_flush(); 449 __gcov_flush();
diff --git a/src/firejail/main.c b/src/firejail/main.c
index e70e20eec..84bf5e8e6 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -35,6 +35,7 @@
35#include <signal.h> 35#include <signal.h>
36#include <time.h> 36#include <time.h>
37#include <net/if.h> 37#include <net/if.h>
38#include <sys/utsname.h>
38 39
39#if 0 40#if 0
40#include <sys/times.h> 41#include <sys/times.h>
@@ -817,8 +818,27 @@ int main(int argc, char **argv) {
817 818
818 if (check_arg(argc, argv, "--quiet")) 819 if (check_arg(argc, argv, "--quiet"))
819 arg_quiet = 1; 820 arg_quiet = 1;
820 if (check_arg(argc, argv, "--allow-debuggers")) 821 if (check_arg(argc, argv, "--allow-debuggers")) {
822 // check kernel version
823 struct utsname u;
824 int rv = uname(&u);
825 if (rv != 0)
826 errExit("uname");
827 int major;
828 int minor;
829 if (2 != sscanf(u.release, "%d.%d", &major, &minor)) {
830 fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version);
831 exit(1);
832 }
833 if (major < 4 || (major == 4 && minor < 8)) {
834 fprintf(stderr, "Error: --allow-debuggers is disabled on Linux kernels prior to 4.8. "
835 "A bug in ptrace call allows a full bypass of the seccomp filter. "
836 "Your current kernel version is %d.%d.\n", major, minor);
837 exit(1);
838 }
839
821 arg_allow_debuggers = 1; 840 arg_allow_debuggers = 1;
841 }
822 842
823 // drop permissions by default and rise them when required 843 // drop permissions by default and rise them when required
824 EUID_INIT(); 844 EUID_INIT();
@@ -1448,13 +1468,10 @@ int main(int argc, char **argv) {
1448 fprintf(stderr, "Error: invalid chroot directory\n"); 1468 fprintf(stderr, "Error: invalid chroot directory\n");
1449 exit(1); 1469 exit(1);
1450 } 1470 }
1451 free(rpath); 1471 cfg.chrootdir = rpath;
1452 1472
1453 // check chroot directory structure 1473 // check chroot directory structure
1454 if (fs_check_chroot_dir(cfg.chrootdir)) { 1474 fs_check_chroot_dir(cfg.chrootdir);
1455 fprintf(stderr, "Error: invalid chroot\n");
1456 exit(1);
1457 }
1458 } 1475 }
1459 else 1476 else
1460 exit_err_feature("chroot"); 1477 exit_err_feature("chroot");
diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c
index d2db7d3dd..e17f39caa 100644
--- a/src/firejail/preproc.c
+++ b/src/firejail/preproc.c
@@ -76,12 +76,12 @@ void preproc_mount_mnt_dir(void) {
76 fs_logger2("tmpfs", RUN_MNT_DIR); 76 fs_logger2("tmpfs", RUN_MNT_DIR);
77 77
78 //copy defaultl seccomp files 78 //copy defaultl seccomp files
79 copy_file(PATH_SECCOMP_I386, RUN_SECCOMP_I386, getuid(), getgid(), 0644); 79 copy_file(PATH_SECCOMP_I386, RUN_SECCOMP_I386, getuid(), getgid(), 0644); // root needed
80 copy_file(PATH_SECCOMP_AMD64, RUN_SECCOMP_AMD64, getuid(), getgid(), 0644); 80 copy_file(PATH_SECCOMP_AMD64, RUN_SECCOMP_AMD64, getuid(), getgid(), 0644); // root needed
81 if (arg_allow_debuggers) 81 if (arg_allow_debuggers)
82 copy_file(PATH_SECCOMP_DEFAULT_DEBUG, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); 82 copy_file(PATH_SECCOMP_DEFAULT_DEBUG, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); // root needed
83 else 83 else
84 copy_file(PATH_SECCOMP_DEFAULT, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); 84 copy_file(PATH_SECCOMP_DEFAULT, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); // root needed
85 85
86 // as root, create an empty RUN_SECCOMP_PROTOCOL file 86 // as root, create an empty RUN_SECCOMP_PROTOCOL file
87 create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); 87 create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644);
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c
index f890dd534..4ec84ec61 100644
--- a/src/firejail/pulseaudio.c
+++ b/src/firejail/pulseaudio.c
@@ -22,6 +22,7 @@
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <sys/mount.h> 23#include <sys/mount.h>
24#include <dirent.h> 24#include <dirent.h>
25#include <sys/wait.h>
25 26
26static void disable_file(const char *path, const char *file) { 27static void disable_file(const char *path, const char *file) {
27 assert(file); 28 assert(file);
@@ -113,7 +114,7 @@ void pulseaudio_init(void) {
113 char *pulsecfg = NULL; 114 char *pulsecfg = NULL;
114 if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1) 115 if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1)
115 errExit("asprintf"); 116 errExit("asprintf");
116 if (copy_file("/etc/pulse/client.conf", pulsecfg, -1, -1, 0644)) 117 if (copy_file("/etc/pulse/client.conf", pulsecfg, -1, -1, 0644)) // root needed
117 errExit("copy_file"); 118 errExit("copy_file");
118 FILE *fp = fopen(pulsecfg, "a+"); 119 FILE *fp = fopen(pulsecfg, "a+");
119 if (!fp) 120 if (!fp)
@@ -127,21 +128,63 @@ void pulseaudio_init(void) {
127 if (asprintf(&dir1, "%s/.config", cfg.homedir) == -1) 128 if (asprintf(&dir1, "%s/.config", cfg.homedir) == -1)
128 errExit("asprintf"); 129 errExit("asprintf");
129 if (stat(dir1, &s) == -1) { 130 if (stat(dir1, &s) == -1) {
130 int rv = mkdir(dir1, 0755); 131 pid_t child = fork();
131 if (rv == 0) { 132 if (child < 0)
132 if (set_perms(dir1, getuid(), getgid(), 0755)) 133 errExit("fork");
133 {;} // do nothing 134 if (child == 0) {
135 // drop privileges
136 drop_privs(0);
137
138 int rv = mkdir(dir1, 0755);
139 if (rv == 0) {
140 if (set_perms(dir1, getuid(), getgid(), 0755))
141 {;} // do nothing
142 }
143#ifdef HAVE_GCOV
144 __gcov_flush();
145#endif
146 _exit(0);
147 }
148 // wait for the child to finish
149 waitpid(child, NULL, 0);
150 }
151 else {
152 // make sure the directory is owned by the user
153 if (s.st_uid != getuid()) {
154 fprintf(stderr, "Error: user .config directory is not owned by the current user\n");
155 exit(1);
134 } 156 }
135 } 157 }
136 free(dir1); 158 free(dir1);
159
137 if (asprintf(&dir1, "%s/.config/pulse", cfg.homedir) == -1) 160 if (asprintf(&dir1, "%s/.config/pulse", cfg.homedir) == -1)
138 errExit("asprintf"); 161 errExit("asprintf");
139 if (stat(dir1, &s) == -1) { 162 if (stat(dir1, &s) == -1) {
140 /* coverity[toctou] */ 163 pid_t child = fork();
141 int rv = mkdir(dir1, 0700); 164 if (child < 0)
142 if (rv == 0) { 165 errExit("fork");
143 if (set_perms(dir1, getuid(), getgid(), 0700)) 166 if (child == 0) {
144 {;} // do nothing 167 // drop privileges
168 drop_privs(0);
169
170 int rv = mkdir(dir1, 0700);
171 if (rv == 0) {
172 if (set_perms(dir1, getuid(), getgid(), 0700))
173 {;} // do nothing
174 }
175#ifdef HAVE_GCOV
176 __gcov_flush();
177#endif
178 _exit(0);
179 }
180 // wait for the child to finish
181 waitpid(child, NULL, 0);
182 }
183 else {
184 // make sure the directory is owned by the user
185 if (s.st_uid != getuid()) {
186 fprintf(stderr, "Error: user .config/pulse directory is not owned by the current user\n");
187 exit(1);
145 } 188 }
146 } 189 }
147 free(dir1); 190 free(dir1);
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 50fcd6ed0..493877db3 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -616,19 +616,10 @@ int sandbox(void* sandbox_arg) {
616 fs_trace_preload(); 616 fs_trace_preload();
617 } 617 }
618 else 618 else
619#endif 619#endif
620#ifdef HAVE_OVERLAYFS 620#ifdef HAVE_OVERLAYFS
621 if (arg_overlay) { 621 if (arg_overlay) {
622 fs_overlayfs(); 622 fs_overlayfs();
623 // force caps and seccomp if not started as root
624 if (getuid() != 0) {
625 enforce_filters();
626#ifdef HAVE_SECCOMP
627 enforce_seccomp = 1;
628#endif
629 }
630 else
631 arg_seccomp = 1;
632 } 623 }
633 else 624 else
634#endif 625#endif
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 75f2acdb9..763e6b58b 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -28,6 +28,7 @@
28#include <grp.h> 28#include <grp.h>
29#include <sys/ioctl.h> 29#include <sys/ioctl.h>
30#include <termios.h> 30#include <termios.h>
31#include <sys/wait.h>
31 32
32#define MAX_GROUPS 1024 33#define MAX_GROUPS 1024
33// drop privileges 34// drop privileges
@@ -177,14 +178,14 @@ int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, m
177 // open source 178 // open source
178 int src = open(srcname, O_RDONLY); 179 int src = open(srcname, O_RDONLY);
179 if (src < 0) { 180 if (src < 0) {
180 fprintf(stderr, "Warning: cannot open %s, file not copied\n", srcname); 181 fprintf(stderr, "Warning: cannot open source file %s, file not copied\n", srcname);
181 return -1; 182 return -1;
182 } 183 }
183 184
184 // open destination 185 // open destination
185 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 186 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
186 if (dst < 0) { 187 if (dst < 0) {
187 fprintf(stderr, "Warning: cannot open %s, file not copied\n", destname); 188 fprintf(stderr, "Warning: cannot open destination file %s, file not copied\n", destname);
188 close(src); 189 close(src);
189 return -1; 190 return -1;
190 } 191 }
@@ -218,6 +219,51 @@ int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, m
218 return 0; 219 return 0;
219} 220}
220 221
222// return -1 if error, 0 if no error
223void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) {
224 pid_t child = fork();
225 if (child < 0)
226 errExit("fork");
227 if (child == 0) {
228 // drop privileges
229 drop_privs(0);
230
231 // copy, set permissions and ownership
232 int rv = copy_file(srcname, destname, uid, gid, mode); // already a regular user
233 if (rv)
234 fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n");
235#ifdef HAVE_GCOV
236 __gcov_flush();
237#endif
238 _exit(0);
239 }
240 // wait for the child to finish
241 waitpid(child, NULL, 0);
242}
243
244// return -1 if error, 0 if no error
245void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode) {
246 pid_t child = fork();
247 if (child < 0)
248 errExit("fork");
249 if (child == 0) {
250 // drop privileges
251 drop_privs(0);
252
253 FILE *fp = fopen(fname, "w");
254 if (fp) {
255 fprintf(fp, "\n");
256 SET_PERMS_STREAM(fp, uid, gid, mode);
257 fclose(fp);
258 }
259#ifdef HAVE_GCOV
260 __gcov_flush();
261#endif
262 _exit(0);
263 }
264 // wait for the child to finish
265 waitpid(child, NULL, 0);
266}
221 267
222// return 1 if the file is a directory 268// return 1 if the file is a directory
223int is_dir(const char *fname) { 269int is_dir(const char *fname) {
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index 60c21cbc1..69d28c788 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -76,7 +76,9 @@ $ firejail [OPTIONS] firefox # starting Mozilla Firefox
76Signal the end of options and disables further option processing. 76Signal the end of options and disables further option processing.
77.TP 77.TP
78\fB\-\-allow-debuggers 78\fB\-\-allow-debuggers
79Allow tools such as strace and gdb inside the sandbox. 79Allow tools such as strace and gdb inside the sandbox. This option is only available
80when running on Linux kernels 4.8 or newer - a kernel bug in ptrace system call allows a full
81bypass of the seccomp filter.
80.br 82.br
81 83
82.br 84.br