aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--RELNOTES2
-rw-r--r--etc/disable-common.inc4
-rw-r--r--etc/evolution.profile3
-rw-r--r--src/firejail/firejail.h4
-rw-r--r--src/firejail/fs.c80
-rw-r--r--src/firejail/fs_home.c148
-rw-r--r--src/firejail/ls.c8
-rw-r--r--src/firejail/main.c7
-rw-r--r--src/firejail/preproc.c8
-rw-r--r--src/firejail/pulseaudio.c3
-rw-r--r--src/firejail/util.c46
11 files changed, 166 insertions, 147 deletions
diff --git a/RELNOTES b/RELNOTES
index a14200a0f..e00eaee00 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -6,7 +6,7 @@ firejail (0.9.45) baseline; urgency=low
6 allows a full bypass of seccomp filter; problem reported by Lizzie Dixon 6 allows a full bypass of seccomp filter; problem reported by Lizzie Dixon
7 (CVE-2017-5206) 7 (CVE-2017-5206)
8 * security: overwrite /etc/resolv.conf found by Martin Carpenter (CVE-2016-10118) 8 * security: overwrite /etc/resolv.conf found by Martin Carpenter (CVE-2016-10118)
9 * secuirty: TOCTOU exploit for --get and --put found by Daniel Hodson 9 * security: TOCTOU exploit for --get and --put found by Daniel Hodson
10 * security: invalid environment exploit found by Martin Carpenter (CVE-2016-10122) 10 * security: invalid environment exploit found by Martin Carpenter (CVE-2016-10122)
11 * security: split most of networking code in a separate executable 11 * security: split most of networking code in a separate executable
12 * security: split seccomp filter code configuration in a separate executable 12 * security: split seccomp filter code configuration in a separate executable
diff --git a/etc/disable-common.inc b/etc/disable-common.inc
index 187d26c83..5a281a91f 100644
--- a/etc/disable-common.inc
+++ b/etc/disable-common.inc
@@ -84,6 +84,7 @@ read-only ${HOME}/.profile
84read-only ${HOME}/.antigen 84read-only ${HOME}/.antigen
85read-only ${HOME}/.bash_login 85read-only ${HOME}/.bash_login
86read-only ${HOME}/.bashrc 86read-only ${HOME}/.bashrc
87read-only ${HOME}/.bash_aliases
87read-only ${HOME}/.bash_profile 88read-only ${HOME}/.bash_profile
88read-only ${HOME}/.bash_logout 89read-only ${HOME}/.bash_logout
89read-only ${HOME}/.zsh.d 90read-only ${HOME}/.zsh.d
@@ -104,6 +105,9 @@ read-only ${HOME}/.caffrc
104read-only ${HOME}/.dotfiles 105read-only ${HOME}/.dotfiles
105read-only ${HOME}/dotfiles 106read-only ${HOME}/dotfiles
106read-only ${HOME}/.mailcap 107read-only ${HOME}/.mailcap
108read-only ${HOME}/.muttrc
109read-only ${HOME}/.mutt/muttrc
110read-only ${HOME}/.msmtprc
107read-only ${HOME}/.exrc 111read-only ${HOME}/.exrc
108read-only ${HOME}/_exrc 112read-only ${HOME}/_exrc
109read-only ${HOME}/.vimrc 113read-only ${HOME}/.vimrc
diff --git a/etc/evolution.profile b/etc/evolution.profile
index ab6dd7a4a..1707e562b 100644
--- a/etc/evolution.profile
+++ b/etc/evolution.profile
@@ -6,6 +6,9 @@ noblacklist ~/.pki
6noblacklist ~/.pki/nssdb 6noblacklist ~/.pki/nssdb
7noblacklist ~/.gnupg 7noblacklist ~/.gnupg
8 8
9noblacklist /var/spool/mail
10noblacklist /var/mail
11
9include /etc/firejail/disable-common.inc 12include /etc/firejail/disable-common.inc
10include /etc/firejail/disable-programs.inc 13include /etc/firejail/disable-programs.inc
11include /etc/firejail/disable-devel.inc 14include /etc/firejail/disable-devel.inc
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 4de082b06..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,7 +105,7 @@ 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;
109 // create an empty file 108 // create an empty file as root, and change ownership to user
110 FILE *fp = fopen(dest, "w"); 109 FILE *fp = fopen(dest, "w");
111 if (fp) { 110 if (fp) {
112 fprintf(fp, "\n"); 111 fprintf(fp, "\n");
@@ -124,27 +123,8 @@ static int store_xauthority(void) {
124 return 0; 123 return 0;
125 } 124 }
126 125
127 pid_t child = fork(); 126 copy_file_as_user(src, dest, getuid(), getgid(), 0600); // regular user
128 if (child < 0) 127 fs_logger2("clone", dest);
129 errExit("fork");
130 if (child == 0) {
131 // drop privileges
132 drop_privs(0);
133
134 // copy, set permissions and ownership
135 int rv = copy_file(src, dest, getuid(), getgid(), 0600);
136 if (rv)
137 fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n");
138 else {
139 fs_logger2("clone", dest);
140 }
141#ifdef HAVE_GCOV
142 __gcov_flush();
143#endif
144 _exit(0);
145 }
146 // wait for the child to finish
147 waitpid(child, NULL, 0);
148 return 1; // file copied 128 return 1; // file copied
149 } 129 }
150 130
@@ -152,9 +132,10 @@ static int store_xauthority(void) {
152} 132}
153 133
154static int store_asoundrc(void) { 134static int store_asoundrc(void) {
135 // put a copy of .Xauthority in XAUTHORITY_FILE
155 char *src; 136 char *src;
156 char *dest = RUN_ASOUNDRC_FILE; 137 char *dest = RUN_ASOUNDRC_FILE;
157 // create an empty file 138 // create an empty file as root, and change ownership to user
158 FILE *fp = fopen(dest, "w"); 139 FILE *fp = fopen(dest, "w");
159 if (fp) { 140 if (fp) {
160 fprintf(fp, "\n"); 141 fprintf(fp, "\n");
@@ -182,27 +163,8 @@ static int store_asoundrc(void) {
182 free(rp); 163 free(rp);
183 } 164 }
184 165
185 pid_t child = fork(); 166 copy_file_as_user(src, dest, getuid(), getgid(), 0644); // regular user
186 if (child < 0) 167 fs_logger2("clone", dest);
187 errExit("fork");
188 if (child == 0) {
189 // drop privileges
190 drop_privs(0);
191
192 // copy, set permissions and ownership
193 int rv = copy_file(src, dest, getuid(), getgid(), 0644);
194 if (rv)
195 fprintf(stderr, "Warning: cannot transfer .asoundrc in private home directory\n");
196 else {
197 fs_logger2("clone", dest);
198 }
199#ifdef HAVE_GCOV
200 __gcov_flush();
201#endif
202 _exit(0);
203 }
204 // wait for the child to finish
205 waitpid(child, NULL, 0);
206 return 1; // file copied 168 return 1; // file copied
207 } 169 }
208 170
@@ -222,27 +184,8 @@ static void copy_xauthority(void) {
222 exit(1); 184 exit(1);
223 } 185 }
224 186
225 pid_t child = fork(); 187 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user
226 if (child < 0) 188 fs_logger2("clone", dest);
227 errExit("fork");
228 if (child == 0) {
229 // drop privileges
230 drop_privs(0);
231
232 // copy, set permissions and ownership
233 int rv = copy_file(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR);
234 if (rv)
235 fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n");
236 else {
237 fs_logger2("clone", dest);
238 }
239#ifdef HAVE_GCOV
240 __gcov_flush();
241#endif
242 _exit(0);
243 }
244 // wait for the child to finish
245 waitpid(child, NULL, 0);
246 189
247 // delete the temporary file 190 // delete the temporary file
248 unlink(src); 191 unlink(src);
@@ -261,27 +204,8 @@ static void copy_asoundrc(void) {
261 exit(1); 204 exit(1);
262 } 205 }
263 206
264 pid_t child = fork(); 207 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user
265 if (child < 0) 208 fs_logger2("clone", dest);
266 errExit("fork");
267 if (child == 0) {
268 // drop privileges
269 drop_privs(0);
270
271 // copy, set permissions and ownership
272 int rv = copy_file(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR);
273 if (rv)
274 fprintf(stderr, "Warning: cannot transfer .asoundrc in private home directory\n");
275 else {
276 fs_logger2("clone", dest);
277 }
278#ifdef HAVE_GCOV
279 __gcov_flush();
280#endif
281 _exit(0);
282 }
283 // wait for the child to finish
284 waitpid(child, NULL, 0);
285 209
286 // delete the temporary file 210 // delete the temporary file
287 unlink(src); 211 unlink(src);
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 3a347b3d9..84bf5e8e6 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -1468,13 +1468,10 @@ int main(int argc, char **argv) {
1468 fprintf(stderr, "Error: invalid chroot directory\n"); 1468 fprintf(stderr, "Error: invalid chroot directory\n");
1469 exit(1); 1469 exit(1);
1470 } 1470 }
1471 free(rpath); 1471 cfg.chrootdir = rpath;
1472 1472
1473 // check chroot directory structure 1473 // check chroot directory structure
1474 if (fs_check_chroot_dir(cfg.chrootdir)) { 1474 fs_check_chroot_dir(cfg.chrootdir);
1475 fprintf(stderr, "Error: invalid chroot\n");
1476 exit(1);
1477 }
1478 } 1475 }
1479 else 1476 else
1480 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 14a7f03dd..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)
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 5b94aa288..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
@@ -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) {