diff options
-rw-r--r-- | src/firejail/fs_home.c | 76 | ||||
-rw-r--r-- | src/firejail/fs_whitelist.c | 5 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 2 | ||||
-rw-r--r-- | src/firejail/util.c | 1 | ||||
-rw-r--r-- | src/firejail/x11.c | 6 |
5 files changed, 61 insertions, 29 deletions
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 04f1a0898..1c4f9e07e 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -35,11 +35,12 @@ | |||
35 | #endif | 35 | #endif |
36 | 36 | ||
37 | static void skel(const char *homedir) { | 37 | static void skel(const char *homedir) { |
38 | char *fname; | 38 | EUID_ASSERT(); |
39 | 39 | ||
40 | // zsh | 40 | // zsh |
41 | if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) { | 41 | if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) { |
42 | // copy skel files | 42 | // copy skel files |
43 | char *fname; | ||
43 | if (asprintf(&fname, "%s/.zshrc", homedir) == -1) | 44 | if (asprintf(&fname, "%s/.zshrc", homedir) == -1) |
44 | errExit("asprintf"); | 45 | errExit("asprintf"); |
45 | // don't copy it if we already have the file | 46 | // don't copy it if we already have the file |
@@ -64,6 +65,7 @@ static void skel(const char *homedir) { | |||
64 | // csh | 65 | // csh |
65 | else if (!arg_shell_none && strcmp(cfg.shell,"/bin/csh") == 0) { | 66 | else if (!arg_shell_none && strcmp(cfg.shell,"/bin/csh") == 0) { |
66 | // copy skel files | 67 | // copy skel files |
68 | char *fname; | ||
67 | if (asprintf(&fname, "%s/.cshrc", homedir) == -1) | 69 | if (asprintf(&fname, "%s/.cshrc", homedir) == -1) |
68 | errExit("asprintf"); | 70 | errExit("asprintf"); |
69 | // don't copy it if we already have the file | 71 | // don't copy it if we already have the file |
@@ -88,6 +90,7 @@ static void skel(const char *homedir) { | |||
88 | // bash etc. | 90 | // bash etc. |
89 | else { | 91 | else { |
90 | // copy skel files | 92 | // copy skel files |
93 | char *fname; | ||
91 | if (asprintf(&fname, "%s/.bashrc", homedir) == -1) | 94 | if (asprintf(&fname, "%s/.bashrc", homedir) == -1) |
92 | errExit("asprintf"); | 95 | errExit("asprintf"); |
93 | // don't copy it if we already have the file | 96 | // don't copy it if we already have the file |
@@ -108,6 +111,7 @@ static void skel(const char *homedir) { | |||
108 | } | 111 | } |
109 | 112 | ||
110 | static int store_xauthority(void) { | 113 | static int store_xauthority(void) { |
114 | EUID_ASSERT(); | ||
111 | if (arg_x11_block) | 115 | if (arg_x11_block) |
112 | return 0; | 116 | return 0; |
113 | 117 | ||
@@ -118,7 +122,7 @@ static int store_xauthority(void) { | |||
118 | errExit("asprintf"); | 122 | errExit("asprintf"); |
119 | 123 | ||
120 | struct stat s; | 124 | struct stat s; |
121 | if (lstat_as_user(src, &s) == 0) { | 125 | if (lstat(src, &s) == 0) { |
122 | if (S_ISLNK(s.st_mode)) { | 126 | if (S_ISLNK(s.st_mode)) { |
123 | fwarning("invalid .Xauthority file\n"); | 127 | fwarning("invalid .Xauthority file\n"); |
124 | free(src); | 128 | free(src); |
@@ -126,6 +130,7 @@ static int store_xauthority(void) { | |||
126 | } | 130 | } |
127 | 131 | ||
128 | // create an empty file as root, and change ownership to user | 132 | // create an empty file as root, and change ownership to user |
133 | EUID_ROOT(); | ||
129 | FILE *fp = fopen(dest, "we"); | 134 | FILE *fp = fopen(dest, "we"); |
130 | if (fp) { | 135 | if (fp) { |
131 | fprintf(fp, "\n"); | 136 | fprintf(fp, "\n"); |
@@ -134,10 +139,11 @@ static int store_xauthority(void) { | |||
134 | } | 139 | } |
135 | else | 140 | else |
136 | errExit("fopen"); | 141 | errExit("fopen"); |
142 | EUID_USER(); | ||
137 | 143 | ||
138 | copy_file_as_user(src, dest, 0600); // regular user | 144 | copy_file_as_user(src, dest, 0600); // regular user |
139 | fs_logger2("clone", dest); | ||
140 | selinux_relabel_path(dest, src); | 145 | selinux_relabel_path(dest, src); |
146 | fs_logger2("clone", dest); | ||
141 | free(src); | 147 | free(src); |
142 | return 1; // file copied | 148 | return 1; // file copied |
143 | } | 149 | } |
@@ -147,6 +153,7 @@ static int store_xauthority(void) { | |||
147 | } | 153 | } |
148 | 154 | ||
149 | static int store_asoundrc(void) { | 155 | static int store_asoundrc(void) { |
156 | EUID_ASSERT(); | ||
150 | if (arg_nosound) | 157 | if (arg_nosound) |
151 | return 0; | 158 | return 0; |
152 | 159 | ||
@@ -157,11 +164,11 @@ static int store_asoundrc(void) { | |||
157 | errExit("asprintf"); | 164 | errExit("asprintf"); |
158 | 165 | ||
159 | struct stat s; | 166 | struct stat s; |
160 | if (lstat_as_user(src, &s) == 0) { | 167 | if (lstat(src, &s) == 0) { |
161 | if (S_ISLNK(s.st_mode)) { | 168 | if (S_ISLNK(s.st_mode)) { |
162 | // make sure the real path of the file is inside the home directory | 169 | // make sure the real path of the file is inside the home directory |
163 | /* coverity[toctou] */ | 170 | /* coverity[toctou] */ |
164 | char *rp = realpath_as_user(src); | 171 | char *rp = realpath(src, NULL); |
165 | if (!rp) { | 172 | if (!rp) { |
166 | fprintf(stderr, "Error: Cannot access %s\n", src); | 173 | fprintf(stderr, "Error: Cannot access %s\n", src); |
167 | exit(1); | 174 | exit(1); |
@@ -174,6 +181,7 @@ static int store_asoundrc(void) { | |||
174 | } | 181 | } |
175 | 182 | ||
176 | // create an empty file as root, and change ownership to user | 183 | // create an empty file as root, and change ownership to user |
184 | EUID_ROOT(); | ||
177 | FILE *fp = fopen(dest, "we"); | 185 | FILE *fp = fopen(dest, "we"); |
178 | if (fp) { | 186 | if (fp) { |
179 | fprintf(fp, "\n"); | 187 | fprintf(fp, "\n"); |
@@ -182,10 +190,11 @@ static int store_asoundrc(void) { | |||
182 | } | 190 | } |
183 | else | 191 | else |
184 | errExit("fopen"); | 192 | errExit("fopen"); |
193 | EUID_USER(); | ||
185 | 194 | ||
186 | copy_file_as_user(src, dest, 0644); // regular user | 195 | copy_file_as_user(src, dest, 0644); // regular user |
187 | selinux_relabel_path(dest, src); | ||
188 | fs_logger2("clone", dest); | 196 | fs_logger2("clone", dest); |
197 | selinux_relabel_path(dest, src); | ||
189 | free(src); | 198 | free(src); |
190 | return 1; // file copied | 199 | return 1; // file copied |
191 | } | 200 | } |
@@ -195,6 +204,7 @@ static int store_asoundrc(void) { | |||
195 | } | 204 | } |
196 | 205 | ||
197 | static void copy_xauthority(void) { | 206 | static void copy_xauthority(void) { |
207 | EUID_ASSERT(); | ||
198 | // copy XAUTHORITY_FILE in the new home directory | 208 | // copy XAUTHORITY_FILE in the new home directory |
199 | char *src = RUN_XAUTHORITY_FILE ; | 209 | char *src = RUN_XAUTHORITY_FILE ; |
200 | char *dest; | 210 | char *dest; |
@@ -208,15 +218,17 @@ static void copy_xauthority(void) { | |||
208 | } | 218 | } |
209 | 219 | ||
210 | copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user | 220 | copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user |
211 | selinux_relabel_path(dest, src); | ||
212 | fs_logger2("clone", dest); | 221 | fs_logger2("clone", dest); |
222 | selinux_relabel_path(dest, dest); | ||
213 | free(dest); | 223 | free(dest); |
214 | 224 | ||
215 | // delete the temporary file | 225 | EUID_ROOT(); |
216 | unlink(src); | 226 | unlink(src); // delete the temporary file |
227 | EUID_USER(); | ||
217 | } | 228 | } |
218 | 229 | ||
219 | static void copy_asoundrc(void) { | 230 | static void copy_asoundrc(void) { |
231 | EUID_ASSERT(); | ||
220 | // copy ASOUNDRC_FILE in the new home directory | 232 | // copy ASOUNDRC_FILE in the new home directory |
221 | char *src = RUN_ASOUNDRC_FILE ; | 233 | char *src = RUN_ASOUNDRC_FILE ; |
222 | char *dest; | 234 | char *dest; |
@@ -230,12 +242,13 @@ static void copy_asoundrc(void) { | |||
230 | } | 242 | } |
231 | 243 | ||
232 | copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user | 244 | copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user |
233 | selinux_relabel_path(dest, src); | ||
234 | fs_logger2("clone", dest); | 245 | fs_logger2("clone", dest); |
246 | selinux_relabel_path(dest, dest); | ||
235 | free(dest); | 247 | free(dest); |
236 | 248 | ||
237 | // delete the temporary file | 249 | EUID_ROOT(); |
238 | unlink(src); | 250 | unlink(src); // delete the temporary file |
251 | EUID_USER(); | ||
239 | } | 252 | } |
240 | 253 | ||
241 | // private mode (--private=homedir): | 254 | // private mode (--private=homedir): |
@@ -248,18 +261,18 @@ void fs_private_homedir(void) { | |||
248 | char *private_homedir = cfg.home_private; | 261 | char *private_homedir = cfg.home_private; |
249 | assert(homedir); | 262 | assert(homedir); |
250 | assert(private_homedir); | 263 | assert(private_homedir); |
251 | 264 | EUID_ASSERT(); | |
252 | int xflag = store_xauthority(); | ||
253 | int aflag = store_asoundrc(); | ||
254 | 265 | ||
255 | uid_t u = getuid(); | 266 | uid_t u = getuid(); |
256 | // gid_t g = getgid(); | 267 | // gid_t g = getgid(); |
257 | 268 | ||
269 | int xflag = store_xauthority(); | ||
270 | int aflag = store_asoundrc(); | ||
271 | |||
258 | // mount bind private_homedir on top of homedir | 272 | // mount bind private_homedir on top of homedir |
259 | if (arg_debug) | 273 | if (arg_debug) |
260 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); | 274 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); |
261 | // get file descriptors for homedir and private_homedir, fails if there is any symlink | 275 | // get file descriptors for homedir and private_homedir, fails if there is any symlink |
262 | EUID_USER(); | ||
263 | int src = safer_openat(-1, private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 276 | int src = safer_openat(-1, private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
264 | if (src == -1) | 277 | if (src == -1) |
265 | errExit("opening private directory"); | 278 | errExit("opening private directory"); |
@@ -287,6 +300,7 @@ void fs_private_homedir(void) { | |||
287 | EUID_ROOT(); | 300 | EUID_ROOT(); |
288 | if (bind_mount_by_fd(src, dst)) | 301 | if (bind_mount_by_fd(src, dst)) |
289 | errExit("mount bind"); | 302 | errExit("mount bind"); |
303 | EUID_USER(); | ||
290 | 304 | ||
291 | // check /proc/self/mountinfo to confirm the mount is ok | 305 | // check /proc/self/mountinfo to confirm the mount is ok |
292 | MountData *mptr = get_last_mount(); | 306 | MountData *mptr = get_last_mount(); |
@@ -305,6 +319,7 @@ void fs_private_homedir(void) { | |||
305 | // if (chmod(homedir, s.st_mode) == -1) | 319 | // if (chmod(homedir, s.st_mode) == -1) |
306 | // errExit("mount-bind chmod"); | 320 | // errExit("mount-bind chmod"); |
307 | 321 | ||
322 | EUID_ROOT(); | ||
308 | if (u != 0) { | 323 | if (u != 0) { |
309 | // mask /root | 324 | // mask /root |
310 | if (arg_debug) | 325 | if (arg_debug) |
@@ -323,6 +338,7 @@ void fs_private_homedir(void) { | |||
323 | selinux_relabel_path("/home", "/home"); | 338 | selinux_relabel_path("/home", "/home"); |
324 | fs_logger("tmpfs /home"); | 339 | fs_logger("tmpfs /home"); |
325 | } | 340 | } |
341 | EUID_USER(); | ||
326 | 342 | ||
327 | skel(homedir); | 343 | skel(homedir); |
328 | if (xflag) | 344 | if (xflag) |
@@ -339,6 +355,7 @@ void fs_private_homedir(void) { | |||
339 | void fs_private(void) { | 355 | void fs_private(void) { |
340 | char *homedir = cfg.homedir; | 356 | char *homedir = cfg.homedir; |
341 | assert(homedir); | 357 | assert(homedir); |
358 | EUID_ASSERT(); | ||
342 | 359 | ||
343 | uid_t u = getuid(); | 360 | uid_t u = getuid(); |
344 | gid_t g = getgid(); | 361 | gid_t g = getgid(); |
@@ -346,6 +363,7 @@ void fs_private(void) { | |||
346 | int xflag = store_xauthority(); | 363 | int xflag = store_xauthority(); |
347 | int aflag = store_asoundrc(); | 364 | int aflag = store_asoundrc(); |
348 | 365 | ||
366 | EUID_ROOT(); | ||
349 | // mask /root | 367 | // mask /root |
350 | if (arg_debug) | 368 | if (arg_debug) |
351 | printf("Mounting a new /root directory\n"); | 369 | printf("Mounting a new /root directory\n"); |
@@ -388,6 +406,7 @@ void fs_private(void) { | |||
388 | 406 | ||
389 | selinux_relabel_path(homedir, homedir); | 407 | selinux_relabel_path(homedir, homedir); |
390 | } | 408 | } |
409 | EUID_USER(); | ||
391 | 410 | ||
392 | skel(homedir); | 411 | skel(homedir); |
393 | if (xflag) | 412 | if (xflag) |
@@ -531,26 +550,29 @@ static void duplicate(char *name) { | |||
531 | // set skel files, | 550 | // set skel files, |
532 | // restore .Xauthority | 551 | // restore .Xauthority |
533 | void fs_private_home_list(void) { | 552 | void fs_private_home_list(void) { |
534 | timetrace_start(); | ||
535 | |||
536 | char *homedir = cfg.homedir; | 553 | char *homedir = cfg.homedir; |
537 | char *private_list = cfg.home_private_keep; | 554 | char *private_list = cfg.home_private_keep; |
538 | assert(homedir); | 555 | assert(homedir); |
539 | assert(private_list); | 556 | assert(private_list); |
557 | EUID_ASSERT(); | ||
540 | 558 | ||
541 | int xflag = store_xauthority(); | 559 | timetrace_start(); |
542 | int aflag = store_asoundrc(); | ||
543 | 560 | ||
544 | uid_t uid = getuid(); | 561 | uid_t uid = getuid(); |
545 | gid_t gid = getgid(); | 562 | gid_t gid = getgid(); |
546 | 563 | ||
564 | int xflag = store_xauthority(); | ||
565 | int aflag = store_asoundrc(); | ||
566 | |||
547 | // create /run/firejail/mnt/home directory | 567 | // create /run/firejail/mnt/home directory |
568 | EUID_ROOT(); | ||
548 | mkdir_attr(RUN_HOME_DIR, 0755, uid, gid); | 569 | mkdir_attr(RUN_HOME_DIR, 0755, uid, gid); |
549 | selinux_relabel_path(RUN_HOME_DIR, homedir); | 570 | selinux_relabel_path(RUN_HOME_DIR, homedir); |
571 | |||
550 | fs_logger_print(); // save the current log | 572 | fs_logger_print(); // save the current log |
573 | EUID_USER(); | ||
551 | 574 | ||
552 | // copy the list of files in the new home directory | 575 | // copy the list of files in the new home directory |
553 | EUID_USER(); | ||
554 | if (arg_debug) | 576 | if (arg_debug) |
555 | printf("Copying files in the new home:\n"); | 577 | printf("Copying files in the new home:\n"); |
556 | char *dlist = strdup(cfg.home_private_keep); | 578 | char *dlist = strdup(cfg.home_private_keep); |
@@ -589,6 +611,7 @@ void fs_private_home_list(void) { | |||
589 | if (bind_mount_path_to_fd(RUN_HOME_DIR, fd)) | 611 | if (bind_mount_path_to_fd(RUN_HOME_DIR, fd)) |
590 | errExit("mount bind"); | 612 | errExit("mount bind"); |
591 | close(fd); | 613 | close(fd); |
614 | EUID_USER(); | ||
592 | 615 | ||
593 | // check /proc/self/mountinfo to confirm the mount is ok | 616 | // check /proc/self/mountinfo to confirm the mount is ok |
594 | MountData *mptr = get_last_mount(); | 617 | MountData *mptr = get_last_mount(); |
@@ -596,11 +619,7 @@ void fs_private_home_list(void) { | |||
596 | errLogExit("invalid private-home mount"); | 619 | errLogExit("invalid private-home mount"); |
597 | fs_logger2("tmpfs", homedir); | 620 | fs_logger2("tmpfs", homedir); |
598 | 621 | ||
599 | // mask RUN_HOME_DIR, it is writable and not noexec | 622 | EUID_ROOT(); |
600 | if (mount("tmpfs", RUN_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
601 | errExit("mounting tmpfs"); | ||
602 | fs_logger2("tmpfs", RUN_HOME_DIR); | ||
603 | |||
604 | if (uid != 0) { | 623 | if (uid != 0) { |
605 | // mask /root | 624 | // mask /root |
606 | if (arg_debug) | 625 | if (arg_debug) |
@@ -620,6 +639,11 @@ void fs_private_home_list(void) { | |||
620 | fs_logger("tmpfs /home"); | 639 | fs_logger("tmpfs /home"); |
621 | } | 640 | } |
622 | 641 | ||
642 | // mask RUN_HOME_DIR, it is writable and not noexec | ||
643 | if (mount("tmpfs", RUN_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
644 | errExit("mounting tmpfs"); | ||
645 | EUID_USER(); | ||
646 | |||
623 | skel(homedir); | 647 | skel(homedir); |
624 | if (xflag) | 648 | if (xflag) |
625 | copy_xauthority(); | 649 | copy_xauthority(); |
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index 370035a4d..7588f69b7 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -374,9 +374,12 @@ static void tmpfs_topdirs(const TopDir *topdirs) { | |||
374 | } | 374 | } |
375 | 375 | ||
376 | // user home directory | 376 | // user home directory |
377 | if (tmpfs_home) | 377 | if (tmpfs_home) { |
378 | // checks owner if outside /home | 378 | // checks owner if outside /home |
379 | EUID_USER(); | ||
379 | fs_private(); | 380 | fs_private(); |
381 | EUID_ROOT(); | ||
382 | } | ||
380 | 383 | ||
381 | // /run/user/$UID directory | 384 | // /run/user/$UID directory |
382 | if (tmpfs_runuser) { | 385 | if (tmpfs_runuser) { |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index e06ba3617..95be3335f 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -840,6 +840,7 @@ int sandbox(void* sandbox_arg) { | |||
840 | // private mode | 840 | // private mode |
841 | //**************************** | 841 | //**************************** |
842 | if (arg_private) { | 842 | if (arg_private) { |
843 | EUID_USER(); | ||
843 | if (cfg.home_private) { // --private= | 844 | if (cfg.home_private) { // --private= |
844 | if (cfg.chrootdir) | 845 | if (cfg.chrootdir) |
845 | fwarning("private=directory feature is disabled in chroot\n"); | 846 | fwarning("private=directory feature is disabled in chroot\n"); |
@@ -858,6 +859,7 @@ int sandbox(void* sandbox_arg) { | |||
858 | } | 859 | } |
859 | else // --private | 860 | else // --private |
860 | fs_private(); | 861 | fs_private(); |
862 | EUID_ROOT(); | ||
861 | } | 863 | } |
862 | 864 | ||
863 | if (arg_private_dev) | 865 | if (arg_private_dev) |
diff --git a/src/firejail/util.c b/src/firejail/util.c index 0921736f1..8fcaf3f7b 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -1231,6 +1231,7 @@ unsigned extract_timeout(const char *str) { | |||
1231 | } | 1231 | } |
1232 | 1232 | ||
1233 | void disable_file_or_dir(const char *fname) { | 1233 | void disable_file_or_dir(const char *fname) { |
1234 | assert(geteuid() == 0); | ||
1234 | assert(fname); | 1235 | assert(fname); |
1235 | 1236 | ||
1236 | EUID_USER(); | 1237 | EUID_USER(); |
diff --git a/src/firejail/x11.c b/src/firejail/x11.c index 0619ff380..896aa2fd3 100644 --- a/src/firejail/x11.c +++ b/src/firejail/x11.c | |||
@@ -1290,9 +1290,11 @@ void x11_xorg(void) { | |||
1290 | if (envar) { | 1290 | if (envar) { |
1291 | char *rp = realpath(envar, NULL); | 1291 | char *rp = realpath(envar, NULL); |
1292 | if (rp) { | 1292 | if (rp) { |
1293 | if (strcmp(rp, dest) != 0) | 1293 | if (strcmp(rp, dest) != 0) { |
1294 | // disable_file_or_dir returns with EUID 0 | 1294 | EUID_ROOT(); |
1295 | disable_file_or_dir(rp); | 1295 | disable_file_or_dir(rp); |
1296 | EUID_USER(); | ||
1297 | } | ||
1296 | free(rp); | 1298 | free(rp); |
1297 | } | 1299 | } |
1298 | } | 1300 | } |