diff options
Diffstat (limited to 'src')
32 files changed, 480 insertions, 451 deletions
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config index a33aaeb49..0bbafb343 100644 --- a/src/firecfg/firecfg.config +++ b/src/firecfg/firecfg.config | |||
@@ -77,6 +77,7 @@ cinelerra | |||
77 | clamdscan | 77 | clamdscan |
78 | clamdtop | 78 | clamdtop |
79 | clamscan | 79 | clamscan |
80 | clamtk | ||
80 | claws-mail | 81 | claws-mail |
81 | clementine | 82 | clementine |
82 | clipit | 83 | clipit |
@@ -328,6 +329,7 @@ pluma | |||
328 | polari | 329 | polari |
329 | ppsspp | 330 | ppsspp |
330 | psi-plus | 331 | psi-plus |
332 | pybitmessage | ||
331 | # pycharm-community - FB note: may enable later | 333 | # pycharm-community - FB note: may enable later |
332 | # pycharm-professional | 334 | # pycharm-professional |
333 | qbittorrent | 335 | qbittorrent |
diff --git a/src/firecfg/main.c b/src/firecfg/main.c index 6fe220d35..298314d4f 100644 --- a/src/firecfg/main.c +++ b/src/firecfg/main.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "firecfg.h" | 21 | #include "firecfg.h" |
22 | #include "../include/firejail_user.h" | 22 | #include "../include/firejail_user.h" |
23 | int arg_debug = 0; | 23 | int arg_debug = 0; |
24 | char *arg_bindir = "/usr/local/bin"; | ||
24 | 25 | ||
25 | static char *usage_str = | 26 | static char *usage_str = |
26 | "Firecfg is the desktop configuration utility for Firejail software. The utility\n" | 27 | "Firecfg is the desktop configuration utility for Firejail software. The utility\n" |
@@ -31,6 +32,7 @@ static char *usage_str = | |||
31 | "DESKTOP INTEGRATION section in man 1 firejail.\n\n" | 32 | "DESKTOP INTEGRATION section in man 1 firejail.\n\n" |
32 | "Usage: firecfg [OPTIONS]\n\n" | 33 | "Usage: firecfg [OPTIONS]\n\n" |
33 | " --add-users user [user] - add the users to Firejail user access database.\n\n" | 34 | " --add-users user [user] - add the users to Firejail user access database.\n\n" |
35 | " --bindir=directory - install in directory instead of /usr/local/bin.\n\n" | ||
34 | " --clean - remove all firejail symbolic links.\n\n" | 36 | " --clean - remove all firejail symbolic links.\n\n" |
35 | " --debug - print debug messages.\n\n" | 37 | " --debug - print debug messages.\n\n" |
36 | " --fix - fix .desktop files.\n\n" | 38 | " --fix - fix .desktop files.\n\n" |
@@ -62,9 +64,9 @@ static void usage(void) { | |||
62 | 64 | ||
63 | 65 | ||
64 | static void list(void) { | 66 | static void list(void) { |
65 | DIR *dir = opendir("/usr/local/bin"); | 67 | DIR *dir = opendir(arg_bindir); |
66 | if (!dir) { | 68 | if (!dir) { |
67 | fprintf(stderr, "Error: cannot open /usr/local/bin directory\n"); | 69 | fprintf(stderr, "Error: cannot open %s directory\n", arg_bindir); |
68 | exit(1); | 70 | exit(1); |
69 | } | 71 | } |
70 | 72 | ||
@@ -78,7 +80,7 @@ static void list(void) { | |||
78 | continue; | 80 | continue; |
79 | 81 | ||
80 | char *fullname; | 82 | char *fullname; |
81 | if (asprintf(&fullname, "/usr/local/bin/%s", entry->d_name) == -1) | 83 | if (asprintf(&fullname, "%s/%s", arg_bindir, entry->d_name) == -1) |
82 | errExit("asprintf"); | 84 | errExit("asprintf"); |
83 | 85 | ||
84 | if (is_link(fullname)) { | 86 | if (is_link(fullname)) { |
@@ -98,14 +100,10 @@ static void list(void) { | |||
98 | 100 | ||
99 | static void clean(void) { | 101 | static void clean(void) { |
100 | printf("Removing all firejail symlinks:\n"); | 102 | printf("Removing all firejail symlinks:\n"); |
101 | if (getuid() != 0) { | ||
102 | fprintf(stderr, "Error: you need to be root to run this command\n"); | ||
103 | exit(1); | ||
104 | } | ||
105 | 103 | ||
106 | DIR *dir = opendir("/usr/local/bin"); | 104 | DIR *dir = opendir(arg_bindir); |
107 | if (!dir) { | 105 | if (!dir) { |
108 | fprintf(stderr, "Error: cannot open /usr/local/bin directory\n"); | 106 | fprintf(stderr, "Error: cannot open %s directory\n", arg_bindir); |
109 | exit(1); | 107 | exit(1); |
110 | } | 108 | } |
111 | 109 | ||
@@ -119,7 +117,7 @@ static void clean(void) { | |||
119 | continue; | 117 | continue; |
120 | 118 | ||
121 | char *fullname; | 119 | char *fullname; |
122 | if (asprintf(&fullname, "/usr/local/bin/%s", entry->d_name) == -1) | 120 | if (asprintf(&fullname, "%s/%s", arg_bindir, entry->d_name) == -1) |
123 | errExit("asprintf"); | 121 | errExit("asprintf"); |
124 | 122 | ||
125 | if (is_link(fullname)) { | 123 | if (is_link(fullname)) { |
@@ -129,8 +127,11 @@ static void clean(void) { | |||
129 | char *ptr = strrchr(fullname, '/'); | 127 | char *ptr = strrchr(fullname, '/'); |
130 | assert(ptr); | 128 | assert(ptr); |
131 | ptr++; | 129 | ptr++; |
132 | unlink(fullname); | 130 | int rv = unlink(fullname); |
133 | printf(" %s removed\n", ptr); | 131 | if (rv) |
132 | fprintf(stderr, "Warning: cannot remove %s\n", fullname); | ||
133 | else | ||
134 | printf(" %s removed\n", ptr); | ||
134 | } | 135 | } |
135 | free(fname); | 136 | free(fname); |
136 | } | 137 | } |
@@ -148,7 +149,7 @@ static void set_file(const char *name, const char *firejail_exec) { | |||
148 | return; | 149 | return; |
149 | 150 | ||
150 | char *fname; | 151 | char *fname; |
151 | if (asprintf(&fname, "/usr/local/bin/%s", name) == -1) | 152 | if (asprintf(&fname, "%s/%s", arg_bindir, name) == -1) |
152 | errExit("asprintf"); | 153 | errExit("asprintf"); |
153 | 154 | ||
154 | struct stat s; | 155 | struct stat s; |
@@ -161,6 +162,9 @@ static void set_file(const char *name, const char *firejail_exec) { | |||
161 | else | 162 | else |
162 | printf(" %s created\n", name); | 163 | printf(" %s created\n", name); |
163 | } | 164 | } |
165 | else { | ||
166 | fprintf(stderr, "Warning: cannot create %s - already exists! Skipping...\n", fname); | ||
167 | } | ||
164 | 168 | ||
165 | free(fname); | 169 | free(fname); |
166 | } | 170 | } |
@@ -181,7 +185,7 @@ static void set_links_firecfg(void) { | |||
181 | fprintf(stderr, "Error: cannot open %s\n", cfgfile); | 185 | fprintf(stderr, "Error: cannot open %s\n", cfgfile); |
182 | exit(1); | 186 | exit(1); |
183 | } | 187 | } |
184 | printf("Configuring symlinks in /usr/local/bin based on firecfg.config\n"); | 188 | printf("Configuring symlinks in %s based on firecfg.config\n", arg_bindir); |
185 | 189 | ||
186 | char buf[MAX_BUF]; | 190 | char buf[MAX_BUF]; |
187 | int lineno = 0; | 191 | int lineno = 0; |
@@ -239,7 +243,7 @@ static void set_links_homedir(const char *homedir) { | |||
239 | errExit("asprintf"); | 243 | errExit("asprintf"); |
240 | 244 | ||
241 | // parse ~/.config/firejail/ directory | 245 | // parse ~/.config/firejail/ directory |
242 | printf("\nConfiguring symlinks in /usr/local/bin based on local firejail config directory\n"); | 246 | printf("\nConfiguring symlinks in %s based on local firejail config directory\n", arg_bindir); |
243 | 247 | ||
244 | DIR *dir = opendir(dirname); | 248 | DIR *dir = opendir(dirname); |
245 | if (!dir) { | 249 | if (!dir) { |
@@ -275,9 +279,68 @@ static void set_links_homedir(const char *homedir) { | |||
275 | free(firejail_exec); | 279 | free(firejail_exec); |
276 | } | 280 | } |
277 | 281 | ||
282 | static char *get_user(void) { | ||
283 | char *user = getlogin(); | ||
284 | if (!user) { | ||
285 | user = getenv("SUDO_USER"); | ||
286 | if (!user) { | ||
287 | fprintf(stderr, "Error: cannot detect login user\n"); | ||
288 | exit(1); | ||
289 | } | ||
290 | } | ||
291 | |||
292 | return user; | ||
293 | } | ||
294 | |||
295 | static char *get_homedir(const char *user, uid_t *uid, gid_t *gid) { | ||
296 | // find home directory | ||
297 | struct passwd *pw = getpwnam(user); | ||
298 | if (!pw) | ||
299 | goto errexit; | ||
300 | |||
301 | char *home = pw->pw_dir; | ||
302 | if (!home) | ||
303 | goto errexit; | ||
304 | |||
305 | *uid = pw->pw_uid; | ||
306 | *gid = pw->pw_gid; | ||
307 | |||
308 | return home; | ||
309 | |||
310 | errexit: | ||
311 | fprintf(stderr, "Error: cannot find home directory for user %s\n", user); | ||
312 | exit(1); | ||
313 | } | ||
278 | 314 | ||
279 | int main(int argc, char **argv) { | 315 | int main(int argc, char **argv) { |
280 | int i; | 316 | int i; |
317 | int bindir_set = 0; | ||
318 | |||
319 | // user setup | ||
320 | char *user = get_user(); | ||
321 | uid_t uid; | ||
322 | gid_t gid; | ||
323 | char *home = get_homedir(user, &uid, &gid); | ||
324 | |||
325 | |||
326 | // check for --bindir | ||
327 | for (i = i; i < argc; i++) { | ||
328 | if (strncmp(argv[i], "--bindir=", 9) == 0) { | ||
329 | if (strncmp(argv[i] + 9, "~/", 2) == 0) { | ||
330 | if (asprintf(&arg_bindir, "%s/%s", home, argv[i] + 11) == -1) | ||
331 | errExit("asprintf"); | ||
332 | } | ||
333 | else | ||
334 | arg_bindir = argv[i] + 9; | ||
335 | bindir_set = 1; | ||
336 | |||
337 | // exit if the directory does not exist, or if we don't have access to it | ||
338 | if (access(arg_bindir, R_OK | W_OK | X_OK)) { | ||
339 | fprintf(stderr, "Error: directory %s not found\n", arg_bindir); | ||
340 | exit(1); | ||
341 | } | ||
342 | } | ||
343 | } | ||
281 | 344 | ||
282 | for (i = 1; i < argc; i++) { | 345 | for (i = 1; i < argc; i++) { |
283 | // default options | 346 | // default options |
@@ -297,15 +360,6 @@ int main(int argc, char **argv) { | |||
297 | return 0; | 360 | return 0; |
298 | } | 361 | } |
299 | else if (strcmp(argv[i], "--fix") == 0) { | 362 | else if (strcmp(argv[i], "--fix") == 0) { |
300 | // find home directory | ||
301 | struct passwd *pw = getpwuid(getuid()); | ||
302 | if (!pw) { | ||
303 | goto errexit; | ||
304 | } | ||
305 | char *home = pw->pw_dir; | ||
306 | if (!home) { | ||
307 | goto errexit; | ||
308 | } | ||
309 | fix_desktop_files(home); | 363 | fix_desktop_files(home); |
310 | return 0; | 364 | return 0; |
311 | } | 365 | } |
@@ -331,19 +385,24 @@ int main(int argc, char **argv) { | |||
331 | return 0; | 385 | return 0; |
332 | } | 386 | } |
333 | else { | 387 | else { |
334 | fprintf(stderr, "Error: invalid command line option\n"); | 388 | if (strncmp(argv[i], "--bindir=", 9) != 0) { // already handled |
335 | usage(); | 389 | fprintf(stderr, "Error: invalid command line option\n"); |
336 | return 1; | 390 | usage(); |
391 | return 1; | ||
392 | } | ||
337 | } | 393 | } |
338 | } | 394 | } |
339 | 395 | ||
396 | if (arg_debug) | ||
397 | printf("%s %d %d %d %d\n", user, getuid(), getgid(), geteuid(), getegid()); | ||
398 | |||
340 | // set symlinks in /usr/local/bin | 399 | // set symlinks in /usr/local/bin |
341 | if (getuid() != 0) { | 400 | if (bindir_set == 0 && getuid() != 0) { |
342 | fprintf(stderr, "Error: cannot set the symbolic links in /usr/local/bin\n"); | 401 | fprintf(stderr, "Error: cannot set the symbolic links in %s\n", arg_bindir); |
343 | fprintf(stderr, "The proper way to run this command is \"sudo firecfg\".\n"); | 402 | fprintf(stderr, "The proper way to run this command is \"sudo firecfg\".\n"); |
344 | return 1; | 403 | return 1; |
345 | } | 404 | } |
346 | else { | 405 | else if (bindir_set == 0) { |
347 | // create /usr/local directory if it doesn't exist (Solus distro) | 406 | // create /usr/local directory if it doesn't exist (Solus distro) |
348 | struct stat s; | 407 | struct stat s; |
349 | if (stat("/usr/local", &s) != 0) { | 408 | if (stat("/usr/local", &s) != 0) { |
@@ -354,66 +413,46 @@ int main(int argc, char **argv) { | |||
354 | return 1; | 413 | return 1; |
355 | } | 414 | } |
356 | } | 415 | } |
357 | if (stat("/usr/local/bin", &s) != 0) { | 416 | if (stat(arg_bindir, &s) != 0) { |
358 | printf("Creating /usr/local directory\n"); | 417 | printf("Creating /usr/local directory\n"); |
359 | int rv = mkdir("/usr/local/bin", 0755); | 418 | int rv = mkdir(arg_bindir, 0755); |
360 | if (rv != 0) { | 419 | if (rv != 0) { |
361 | fprintf(stderr, "Error: cannot create /usr/local/bin directory\n"); | 420 | fprintf(stderr, "Error: cannot create %s directory\n", arg_bindir); |
362 | return 1; | 421 | return 1; |
363 | } | 422 | } |
364 | } | 423 | } |
365 | } | 424 | } |
366 | clean(); | ||
367 | set_links_firecfg(); | ||
368 | |||
369 | 425 | ||
426 | // clear all symlinks | ||
427 | clean(); | ||
370 | 428 | ||
371 | // user setup | 429 | // set new symlinks based on /usr/lib/firejail/firecfg.cfg |
372 | char *user = getlogin(); | 430 | set_links_firecfg(); |
373 | if (!user) { | ||
374 | user = getenv("SUDO_USER"); | ||
375 | if (!user) { | ||
376 | goto errexit; | ||
377 | } | ||
378 | } | ||
379 | 431 | ||
380 | // add user to firejail access database | 432 | // add user to firejail access database - only for root |
381 | if (user) { | 433 | if (user && getuid() == 0) { |
382 | printf("\nAdding user %s to Firejail access database in %s/firejail.users\n", user, SYSCONFDIR); | 434 | printf("\nAdding user %s to Firejail access database in %s/firejail.users\n", user, SYSCONFDIR); |
383 | firejail_user_add(user); | 435 | firejail_user_add(user); |
384 | } | 436 | } |
385 | 437 | ||
386 | // switch to the local user, and fix desktop files | 438 | // set new symlinks based on ~/.config/firejail directory |
387 | if (user) { | 439 | set_links_homedir(home); |
388 | // find home directory | ||
389 | struct passwd *pw = getpwnam(user); | ||
390 | if (!pw) { | ||
391 | goto errexit; | ||
392 | } | ||
393 | char *home = pw->pw_dir; | ||
394 | if (!home) { | ||
395 | goto errexit; | ||
396 | } | ||
397 | |||
398 | // running as root | ||
399 | set_links_homedir(home); | ||
400 | 440 | ||
401 | // drop permissions | 441 | // drop permissions |
442 | if (getuid() == 0) { | ||
402 | if (setgroups(0, NULL) < 0) | 443 | if (setgroups(0, NULL) < 0) |
403 | errExit("setgroups"); | 444 | errExit("setgroups"); |
404 | // set uid/gid | 445 | if (setgid(gid) < 0) |
405 | if (setgid(pw->pw_gid) < 0) | ||
406 | errExit("setgid"); | 446 | errExit("setgid"); |
407 | if (setuid(pw->pw_uid) < 0) | 447 | if (setuid(uid) < 0) |
408 | errExit("setuid"); | 448 | errExit("setuid"); |
409 | if (arg_debug) | ||
410 | printf("%s %d %d %d %d\n", user, getuid(), getgid(), geteuid(), getegid()); | ||
411 | fix_desktop_files(home); | ||
412 | } | 449 | } |
413 | 450 | ||
414 | return 0; | 451 | if (arg_debug) |
452 | printf("%s %d %d %d %d\n", user, getuid(), getgid(), geteuid(), getegid()); | ||
415 | 453 | ||
416 | errexit: | 454 | // fix .desktop files in ~/.local/share/applications directory |
417 | fprintf(stderr, "Error: cannot detect login user in order to set desktop files in ~/.local/share/applications\n"); | 455 | fix_desktop_files(home); |
418 | return 1; | 456 | |
457 | return 0; | ||
419 | } | 458 | } |
diff --git a/src/firejail/arg-checking.txt b/src/firejail/arg-checking.txt deleted file mode 100644 index cfed454f8..000000000 --- a/src/firejail/arg-checking.txt +++ /dev/null | |||
@@ -1,84 +0,0 @@ | |||
1 | arg checking: | ||
2 | |||
3 | 1. --output=filename | ||
4 | - not supported in profiles | ||
5 | - checking no "..", | ||
6 | - checking no link, | ||
7 | - checking no dir, | ||
8 | - checking same permissions, | ||
9 | - checking no hard links | ||
10 | - unit test | ||
11 | |||
12 | 2. --chroot=dirname | ||
13 | - not supported in profiles | ||
14 | - expand "~" | ||
15 | - checking no "..", | ||
16 | - checking is dir, | ||
17 | - checking no link | ||
18 | - checking directory structure | ||
19 | - unit test | ||
20 | |||
21 | 3. --bind=dirname1,dirname2, --bind=filename1,filenam2 | ||
22 | - supported in profiles | ||
23 | - accepted only when running as root | ||
24 | - checking string chars | ||
25 | - checking no ".." | ||
26 | - unit test non root | ||
27 | |||
28 | 4. --tmpfs=dirname | ||
29 | - supported in profiles | ||
30 | - checking string chars | ||
31 | - checking no ".." | ||
32 | - unit test | ||
33 | |||
34 | 5. --blacklist=filename, --blacklist=dirname | ||
35 | - supported in profiles | ||
36 | - checking string chars | ||
37 | - checking no ".." | ||
38 | - unit test | ||
39 | |||
40 | 6. --read-only=filename, --read-only=dirname | ||
41 | - supported in profiles | ||
42 | - checking string chars | ||
43 | - checking no ".." | ||
44 | - unit test | ||
45 | |||
46 | 7. --profile=filename | ||
47 | - check access as real GID/UID | ||
48 | - checking no dir | ||
49 | - checking no link | ||
50 | - checking no ".." | ||
51 | - unit test | ||
52 | |||
53 | 8. --private=dirname | ||
54 | - supported in profiles | ||
55 | - expand "~" | ||
56 | - check is dir | ||
57 | - check no link | ||
58 | - checking no ".." | ||
59 | - check same owner | ||
60 | - unit test | ||
61 | |||
62 | 9. --private-home=filelist | ||
63 | - supported in profiles | ||
64 | - checking no ".." | ||
65 | - checking file found | ||
66 | - checking same owner | ||
67 | - checking no link | ||
68 | - unit test | ||
69 | |||
70 | 10. --netfilter=filename | ||
71 | - supported in profiles | ||
72 | - check access as real GID/UID | ||
73 | - checking no dir | ||
74 | - checking no link | ||
75 | - checking no ".." | ||
76 | - unit test | ||
77 | |||
78 | 11. --shell=filename | ||
79 | - not supported in profiles | ||
80 | - check access as real GID/UID | ||
81 | - checking no dir | ||
82 | - checking no link | ||
83 | - checking no ".." | ||
84 | - unit test | ||
diff --git a/src/firejail/bandwidth.c b/src/firejail/bandwidth.c index 0045b444f..d7764682a 100644 --- a/src/firejail/bandwidth.c +++ b/src/firejail/bandwidth.c | |||
@@ -328,7 +328,12 @@ void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, in | |||
328 | // join the network namespace | 328 | // join the network namespace |
329 | //************************ | 329 | //************************ |
330 | pid_t child; | 330 | pid_t child; |
331 | if (find_child(pid, &child) == -1) { | 331 | if (find_child(pid, &child) == 1) { |
332 | fprintf(stderr, "Error: cannot join the network namespace\n"); | ||
333 | exit(1); | ||
334 | } | ||
335 | |||
336 | if (invalid_sandbox(child)) { | ||
332 | fprintf(stderr, "Error: cannot join the network namespace\n"); | 337 | fprintf(stderr, "Error: cannot join the network namespace\n"); |
333 | exit(1); | 338 | exit(1); |
334 | } | 339 | } |
diff --git a/src/firejail/caps.c b/src/firejail/caps.c index dae45d9df..bd3b5e229 100644 --- a/src/firejail/caps.c +++ b/src/firejail/caps.c | |||
@@ -401,18 +401,13 @@ errexit: | |||
401 | void caps_print_filter(pid_t pid) { | 401 | void caps_print_filter(pid_t pid) { |
402 | EUID_ASSERT(); | 402 | EUID_ASSERT(); |
403 | 403 | ||
404 | // if the pid is that of a firejail process, use the pid of the first child process | 404 | // in case the pid is that of a firejail process, use the pid of the first child process |
405 | EUID_ROOT(); // grsecurity | 405 | pid = switch_to_child(pid); |
406 | char *comm = pid_proc_comm(pid); | 406 | |
407 | EUID_USER(); // grsecurity | 407 | // now check if the pid belongs to a firejail sandbox |
408 | if (comm) { | 408 | if (invalid_sandbox(pid)) { |
409 | if (strcmp(comm, "firejail") == 0) { | 409 | fprintf(stderr, "Error: no valid sandbox\n"); |
410 | pid_t child; | 410 | exit(1); |
411 | if (find_child(pid, &child) == 0) { | ||
412 | pid = child; | ||
413 | } | ||
414 | } | ||
415 | free(comm); | ||
416 | } | 411 | } |
417 | 412 | ||
418 | // check privileges for non-root users | 413 | // check privileges for non-root users |
diff --git a/src/firejail/cpu.c b/src/firejail/cpu.c index 8f72fb69e..a92562e67 100644 --- a/src/firejail/cpu.c +++ b/src/firejail/cpu.c | |||
@@ -165,18 +165,13 @@ static void print_cpu(int pid) { | |||
165 | void cpu_print_filter(pid_t pid) { | 165 | void cpu_print_filter(pid_t pid) { |
166 | EUID_ASSERT(); | 166 | EUID_ASSERT(); |
167 | 167 | ||
168 | // if the pid is that of a firejail process, use the pid of the first child process | 168 | // in case the pid is that of a firejail process, use the pid of the first child process |
169 | EUID_ROOT(); // grsecurity | 169 | pid = switch_to_child(pid); |
170 | char *comm = pid_proc_comm(pid); | 170 | |
171 | EUID_USER(); // grsecurity | 171 | // now check if the pid belongs to a firejail sandbox |
172 | if (comm) { | 172 | if (invalid_sandbox(pid)) { |
173 | if (strcmp(comm, "firejail") == 0) { | 173 | fprintf(stderr, "Error: no valid sandbox\n"); |
174 | pid_t child; | 174 | exit(1); |
175 | if (find_child(pid, &child) == 0) { | ||
176 | pid = child; | ||
177 | } | ||
178 | } | ||
179 | free(comm); | ||
180 | } | 175 | } |
181 | 176 | ||
182 | // check privileges for non-root users | 177 | // check privileges for non-root users |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index f31d6a2bc..051456539 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -100,6 +100,7 @@ | |||
100 | #define RUN_FSLOGGER_FILE "/run/firejail/mnt/fslogger" | 100 | #define RUN_FSLOGGER_FILE "/run/firejail/mnt/fslogger" |
101 | #define RUN_UMASK_FILE "/run/firejail/mnt/umask" | 101 | #define RUN_UMASK_FILE "/run/firejail/mnt/umask" |
102 | #define RUN_OVERLAY_ROOT "/run/firejail/mnt/oroot" | 102 | #define RUN_OVERLAY_ROOT "/run/firejail/mnt/oroot" |
103 | #define RUN_READY_FOR_JOIN "/run/firejail/mnt/ready-for-join" | ||
103 | 104 | ||
104 | 105 | ||
105 | // profiles | 106 | // profiles |
@@ -405,7 +406,7 @@ char *guess_shell(void); | |||
405 | 406 | ||
406 | // sandbox.c | 407 | // sandbox.c |
407 | int sandbox(void* sandbox_arg); | 408 | int sandbox(void* sandbox_arg); |
408 | void start_application(int no_sandbox); | 409 | void start_application(int no_sandbox, FILE *fp); |
409 | 410 | ||
410 | // network_main.c | 411 | // network_main.c |
411 | void net_configure_sandbox_ip(Bridge *br); | 412 | void net_configure_sandbox_ip(Bridge *br); |
@@ -477,6 +478,7 @@ void usage(void); | |||
477 | 478 | ||
478 | // join.c | 479 | // join.c |
479 | void join(pid_t pid, int argc, char **argv, int index); | 480 | void join(pid_t pid, int argc, char **argv, int index); |
481 | pid_t switch_to_child(pid_t pid); | ||
480 | 482 | ||
481 | // shutdown.c | 483 | // shutdown.c |
482 | void shut(pid_t pid); | 484 | void shut(pid_t pid); |
@@ -512,9 +514,10 @@ void logerr(const char *msg); | |||
512 | int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); | 514 | int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); |
513 | void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); | 515 | void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); |
514 | void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); | 516 | void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); |
515 | void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode); | 517 | void touch_file_as_user(const char *fname, mode_t mode); |
516 | int is_dir(const char *fname); | 518 | int is_dir(const char *fname); |
517 | int is_link(const char *fname); | 519 | int is_link(const char *fname); |
520 | void trim_trailing_slash_or_dot(char *path); | ||
518 | char *line_remove_spaces(const char *buf); | 521 | char *line_remove_spaces(const char *buf); |
519 | char *split_comma(char *str); | 522 | char *split_comma(char *str); |
520 | void check_unsigned(const char *str, const char *msg); | 523 | void check_unsigned(const char *str, const char *msg); |
@@ -536,6 +539,7 @@ unsigned extract_timeout(const char *str); | |||
536 | void disable_file_or_dir(const char *fname); | 539 | void disable_file_or_dir(const char *fname); |
537 | void disable_file_path(const char *path, const char *file); | 540 | void disable_file_path(const char *path, const char *file); |
538 | int safe_fd(const char *path, int flags); | 541 | int safe_fd(const char *path, int flags); |
542 | int invalid_sandbox(const pid_t pid); | ||
539 | 543 | ||
540 | // Get info regarding the last kernel mount operation from /proc/self/mountinfo | 544 | // Get info regarding the last kernel mount operation from /proc/self/mountinfo |
541 | // The return value points to a static area, and will be overwritten by subsequent calls. | 545 | // The return value points to a static area, and will be overwritten by subsequent calls. |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index ba2f8e284..d28ff534f 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -641,8 +641,26 @@ void fs_proc_sys_dev_boot(void) { | |||
641 | char *fnamegpg; | 641 | char *fnamegpg; |
642 | if (asprintf(&fnamegpg, "/run/user/%d/gnupg", getuid()) == -1) | 642 | if (asprintf(&fnamegpg, "/run/user/%d/gnupg", getuid()) == -1) |
643 | errExit("asprintf"); | 643 | errExit("asprintf"); |
644 | if (stat(fnamegpg, &s) == -1) | 644 | if (stat(fnamegpg, &s) == -1) { |
645 | mkdir_attr(fnamegpg, 0700, getuid(), getgid()); | 645 | pid_t child = fork(); |
646 | if (child < 0) | ||
647 | errExit("fork"); | ||
648 | if (child == 0) { | ||
649 | // drop privileges | ||
650 | drop_privs(0); | ||
651 | if (mkdir(fnamegpg, 0700) == 0) { | ||
652 | if (chmod(fnamegpg, 0700) == -1) | ||
653 | {;} // do nothing | ||
654 | } | ||
655 | #ifdef HAVE_GCOV | ||
656 | __gcov_flush(); | ||
657 | #endif | ||
658 | _exit(0); | ||
659 | } | ||
660 | // wait for the child to finish | ||
661 | waitpid(child, NULL, 0); | ||
662 | fs_logger2("create", fnamegpg); | ||
663 | } | ||
646 | if (stat(fnamegpg, &s) == 0) | 664 | if (stat(fnamegpg, &s) == 0) |
647 | disable_file(BLACKLIST_FILE, fnamegpg); | 665 | disable_file(BLACKLIST_FILE, fnamegpg); |
648 | free(fnamegpg); | 666 | free(fnamegpg); |
@@ -651,8 +669,26 @@ void fs_proc_sys_dev_boot(void) { | |||
651 | char *fnamesysd; | 669 | char *fnamesysd; |
652 | if (asprintf(&fnamesysd, "/run/user/%d/systemd", getuid()) == -1) | 670 | if (asprintf(&fnamesysd, "/run/user/%d/systemd", getuid()) == -1) |
653 | errExit("asprintf"); | 671 | errExit("asprintf"); |
654 | if (stat(fnamesysd, &s) == -1) | 672 | if (stat(fnamesysd, &s) == -1) { |
655 | mkdir_attr(fnamesysd, 0755, getuid(), getgid()); | 673 | pid_t child = fork(); |
674 | if (child < 0) | ||
675 | errExit("fork"); | ||
676 | if (child == 0) { | ||
677 | // drop privileges | ||
678 | drop_privs(0); | ||
679 | if (mkdir(fnamesysd, 0755) == 0) { | ||
680 | if (chmod(fnamesysd, 0755) == -1) | ||
681 | {;} // do nothing | ||
682 | } | ||
683 | #ifdef HAVE_GCOV | ||
684 | __gcov_flush(); | ||
685 | #endif | ||
686 | _exit(0); | ||
687 | } | ||
688 | // wait for the child to finish | ||
689 | waitpid(child, NULL, 0); | ||
690 | fs_logger2("create", fnamesysd); | ||
691 | } | ||
656 | if (stat(fnamesysd, &s) == 0) | 692 | if (stat(fnamesysd, &s) == 0) |
657 | disable_file(BLACKLIST_FILE, fnamesysd); | 693 | disable_file(BLACKLIST_FILE, fnamesysd); |
658 | free(fnamesysd); | 694 | free(fnamesysd); |
@@ -1347,14 +1383,17 @@ void fs_private_cache(void) { | |||
1347 | struct stat s; | 1383 | struct stat s; |
1348 | if (is_link(cache)) { | 1384 | if (is_link(cache)) { |
1349 | fwarning("user .cache is a symbolic link, tmpfs not mounted\n"); | 1385 | fwarning("user .cache is a symbolic link, tmpfs not mounted\n"); |
1386 | free(cache); | ||
1350 | return; | 1387 | return; |
1351 | } | 1388 | } |
1352 | if (stat(cache, &s) == -1 || !S_ISDIR(s.st_mode)) { | 1389 | if (stat(cache, &s) == -1 || !S_ISDIR(s.st_mode)) { |
1353 | fwarning("no user .cache directory found, tmpfs not mounted\n"); | 1390 | fwarning("no user .cache directory found, tmpfs not mounted\n"); |
1391 | free(cache); | ||
1354 | return; | 1392 | return; |
1355 | } | 1393 | } |
1356 | if (s.st_uid != getuid()) { | 1394 | if (s.st_uid != getuid()) { |
1357 | fwarning("user .cache is not owned by current user, tmpfs not mounted\n"); | 1395 | fwarning("user .cache is not owned by current user, tmpfs not mounted\n"); |
1396 | free(cache); | ||
1358 | return; | 1397 | return; |
1359 | } | 1398 | } |
1360 | 1399 | ||
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c index 01350aa0e..8e8739436 100644 --- a/src/firejail/fs_etc.c +++ b/src/firejail/fs_etc.c | |||
@@ -100,7 +100,9 @@ errexit: | |||
100 | } | 100 | } |
101 | 101 | ||
102 | static void duplicate(const char *fname, const char *private_dir, const char *private_run_dir) { | 102 | static void duplicate(const char *fname, const char *private_dir, const char *private_run_dir) { |
103 | if (*fname == '~' || *fname == '/' || strstr(fname, "..")) { | 103 | assert(fname); |
104 | |||
105 | if (*fname == '~' || strchr(fname, '/') || strcmp(fname, "..") == 0) { | ||
104 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); | 106 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); |
105 | exit(1); | 107 | exit(1); |
106 | } | 108 | } |
@@ -163,6 +165,10 @@ void fs_private_dir_list(const char *private_dir, const char *private_run_dir, c | |||
163 | 165 | ||
164 | 166 | ||
165 | char *ptr = strtok(dlist, ","); | 167 | char *ptr = strtok(dlist, ","); |
168 | if (!ptr) { | ||
169 | fprintf(stderr, "Error: invalid private %s argument\n", private_dir); | ||
170 | exit(1); | ||
171 | } | ||
166 | duplicate(ptr, private_dir, private_run_dir); | 172 | duplicate(ptr, private_dir, private_run_dir); |
167 | 173 | ||
168 | while ((ptr = strtok(NULL, ",")) != NULL) | 174 | while ((ptr = strtok(NULL, ",")) != NULL) |
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 3a332f7ff..866b750b0 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -53,7 +53,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
53 | fs_logger2("clone", fname); | 53 | fs_logger2("clone", fname); |
54 | } | 54 | } |
55 | else { | 55 | else { |
56 | touch_file_as_user(fname, u, g, 0644); | 56 | touch_file_as_user(fname, 0644); |
57 | fs_logger2("touch", fname); | 57 | fs_logger2("touch", fname); |
58 | } | 58 | } |
59 | free(fname); | 59 | free(fname); |
@@ -78,7 +78,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
78 | fs_logger2("clone", fname); | 78 | fs_logger2("clone", fname); |
79 | } | 79 | } |
80 | else { | 80 | else { |
81 | touch_file_as_user(fname, u, g, 0644); | 81 | touch_file_as_user(fname, 0644); |
82 | fs_logger2("touch", fname); | 82 | fs_logger2("touch", fname); |
83 | } | 83 | } |
84 | free(fname); | 84 | free(fname); |
@@ -235,8 +235,29 @@ void fs_private_homedir(void) { | |||
235 | // mount bind private_homedir on top of homedir | 235 | // mount bind private_homedir on top of homedir |
236 | if (arg_debug) | 236 | if (arg_debug) |
237 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); | 237 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); |
238 | if (mount(private_homedir, homedir, NULL, MS_NOSUID | MS_NODEV | MS_BIND | MS_REC, NULL) < 0) | 238 | // get a file descriptor for private_homedir, fails if there is any symlink |
239 | int fd = safe_fd(private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
240 | if (fd == -1) | ||
241 | errExit("safe_fd"); | ||
242 | // check if new home directory is owned by the user | ||
243 | struct stat s; | ||
244 | if (fstat(fd, &s) == -1) | ||
245 | errExit("fstat"); | ||
246 | if (s.st_uid != getuid()) { | ||
247 | fprintf(stderr, "Error: private directory is not owned by the current user\n"); | ||
248 | exit(1); | ||
249 | } | ||
250 | if ((S_IRWXU & s.st_mode) != S_IRWXU) | ||
251 | fwarning("no full permissions for private directory\n"); | ||
252 | // mount via the link in /proc/self/fd | ||
253 | char *proc; | ||
254 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
255 | errExit("asprintf"); | ||
256 | if (mount(proc, homedir, NULL, MS_NOSUID | MS_NODEV | MS_BIND | MS_REC, NULL) < 0) | ||
239 | errExit("mount bind"); | 257 | errExit("mount bind"); |
258 | free(proc); | ||
259 | close(fd); | ||
260 | |||
240 | fs_logger3("mount-bind", private_homedir, cfg.homedir); | 261 | fs_logger3("mount-bind", private_homedir, cfg.homedir); |
241 | fs_logger2("whitelist", cfg.homedir); | 262 | fs_logger2("whitelist", cfg.homedir); |
242 | // preserve mode and ownership | 263 | // preserve mode and ownership |
@@ -339,37 +360,16 @@ void fs_check_private_dir(void) { | |||
339 | free(tmp); | 360 | free(tmp); |
340 | 361 | ||
341 | if (!cfg.home_private | 362 | if (!cfg.home_private |
342 | || !is_dir(cfg.home_private) | 363 | || !is_dir(cfg.home_private)) { |
343 | || is_link(cfg.home_private) | ||
344 | || strstr(cfg.home_private, "..")) { | ||
345 | fprintf(stderr, "Error: invalid private directory\n"); | 364 | fprintf(stderr, "Error: invalid private directory\n"); |
346 | exit(1); | 365 | exit(1); |
347 | } | 366 | } |
348 | |||
349 | // check home directory and chroot home directory have the same owner | ||
350 | struct stat s2; | ||
351 | int rv = stat(cfg.home_private, &s2); | ||
352 | if (rv < 0) { | ||
353 | fprintf(stderr, "Error: cannot find %s directory\n", cfg.home_private); | ||
354 | exit(1); | ||
355 | } | ||
356 | |||
357 | struct stat s1; | ||
358 | rv = stat(cfg.homedir, &s1); | ||
359 | if (rv < 0) { | ||
360 | fprintf(stderr, "Error: cannot find %s directory, full path name required\n", cfg.homedir); | ||
361 | exit(1); | ||
362 | } | ||
363 | if (s1.st_uid != s2.st_uid) { | ||
364 | printf("Error: --private directory should be owned by the current user\n"); | ||
365 | exit(1); | ||
366 | } | ||
367 | } | 367 | } |
368 | 368 | ||
369 | #ifndef LTS | ||
370 | //*********************************************************************************** | 369 | //*********************************************************************************** |
371 | // --private-home | 370 | // --private-home |
372 | //*********************************************************************************** | 371 | //*********************************************************************************** |
372 | #ifndef LTS | ||
373 | static char *check_dir_or_file(const char *name) { | 373 | static char *check_dir_or_file(const char *name) { |
374 | assert(name); | 374 | assert(name); |
375 | 375 | ||
@@ -401,34 +401,33 @@ static char *check_dir_or_file(const char *name) { | |||
401 | } | 401 | } |
402 | return fname; | 402 | return fname; |
403 | } | 403 | } |
404 | else { | 404 | else // dangling link |
405 | fprintf(stderr, "Error: invalid file %s\n", name); | 405 | goto errexit; |
406 | exit(1); | ||
407 | } | ||
408 | } | 406 | } |
409 | else { | 407 | else { |
410 | // check the file is in user home directory, a full home directory is not allowed | 408 | // check the file is in user home directory, a full home directory is not allowed |
411 | char *rname = realpath(fname, NULL); | 409 | char *rname = realpath(fname, NULL); |
412 | if (!rname || | 410 | if (!rname || |
413 | strncmp(rname, cfg.homedir, strlen(cfg.homedir)) != 0 || | 411 | strncmp(rname, cfg.homedir, strlen(cfg.homedir)) != 0 || |
414 | strcmp(rname, cfg.homedir) == 0) { | 412 | strcmp(rname, cfg.homedir) == 0) |
415 | fprintf(stderr, "Error: invalid file %s\n", name); | 413 | goto errexit; |
416 | exit(1); | ||
417 | } | ||
418 | 414 | ||
419 | // only top files and directories in user home are allowed | 415 | // only top files and directories in user home are allowed |
420 | char *ptr = rname + strlen(cfg.homedir); | 416 | char *ptr = rname + strlen(cfg.homedir); |
421 | assert(*ptr != '\0'); | 417 | if (*ptr != '/') |
418 | goto errexit; | ||
422 | ptr = strchr(++ptr, '/'); | 419 | ptr = strchr(++ptr, '/'); |
423 | if (ptr) { | 420 | if (ptr) { |
424 | if (*ptr != '\0') { | 421 | fprintf(stderr, "Error: only top files and directories in user home are allowed\n"); |
425 | fprintf(stderr, "Error: only top files and directories in user home are allowed\n"); | 422 | exit(1); |
426 | exit(1); | ||
427 | } | ||
428 | } | 423 | } |
429 | free(fname); | 424 | free(fname); |
430 | return rname; | 425 | return rname; |
431 | } | 426 | } |
427 | |||
428 | errexit: | ||
429 | fprintf(stderr, "Error: invalid file %s\n", name); | ||
430 | exit(1); | ||
432 | } | 431 | } |
433 | 432 | ||
434 | static void duplicate(char *name) { | 433 | static void duplicate(char *name) { |
@@ -495,6 +494,10 @@ void fs_private_home_list(void) { | |||
495 | errExit("strdup"); | 494 | errExit("strdup"); |
496 | 495 | ||
497 | char *ptr = strtok(dlist, ","); | 496 | char *ptr = strtok(dlist, ","); |
497 | if (!ptr) { | ||
498 | fprintf(stderr, "Error: invalid private-home argument\n"); | ||
499 | exit(1); | ||
500 | } | ||
498 | duplicate(ptr); | 501 | duplicate(ptr); |
499 | while ((ptr = strtok(NULL, ",")) != NULL) | 502 | while ((ptr = strtok(NULL, ",")) != NULL) |
500 | duplicate(ptr); | 503 | duplicate(ptr); |
diff --git a/src/firejail/fs_logger.c b/src/firejail/fs_logger.c index 93f28a26b..02e2ba5d7 100644 --- a/src/firejail/fs_logger.c +++ b/src/firejail/fs_logger.c | |||
@@ -120,19 +120,8 @@ void fs_logger_change_owner(void) { | |||
120 | void fs_logger_print_log(pid_t pid) { | 120 | void fs_logger_print_log(pid_t pid) { |
121 | EUID_ASSERT(); | 121 | EUID_ASSERT(); |
122 | 122 | ||
123 | // if the pid is that of a firejail process, use the pid of the first child process | 123 | // in case the pid is that of a firejail process, use the pid of the first child process |
124 | EUID_ROOT(); | 124 | pid = switch_to_child(pid); |
125 | char *comm = pid_proc_comm(pid); | ||
126 | EUID_USER(); | ||
127 | if (comm) { | ||
128 | if (strcmp(comm, "firejail") == 0) { | ||
129 | pid_t child; | ||
130 | if (find_child(pid, &child) == 0) { | ||
131 | pid = child; | ||
132 | } | ||
133 | } | ||
134 | free(comm); | ||
135 | } | ||
136 | 125 | ||
137 | // check privileges for non-root users | 126 | // check privileges for non-root users |
138 | uid_t uid = getuid(); | 127 | uid_t uid = getuid(); |
@@ -151,7 +140,7 @@ void fs_logger_print_log(pid_t pid) { | |||
151 | 140 | ||
152 | EUID_ROOT(); | 141 | EUID_ROOT(); |
153 | struct stat s; | 142 | struct stat s; |
154 | if (stat(fname, &s) == -1) { | 143 | if (stat(fname, &s) == -1 || s.st_uid != 0) { |
155 | fprintf(stderr, "Error: Cannot access filesystem log\n"); | 144 | fprintf(stderr, "Error: Cannot access filesystem log\n"); |
156 | exit(1); | 145 | exit(1); |
157 | } | 146 | } |
diff --git a/src/firejail/fs_mkdir.c b/src/firejail/fs_mkdir.c index 9d22093ee..b66068a95 100644 --- a/src/firejail/fs_mkdir.c +++ b/src/firejail/fs_mkdir.c | |||
@@ -114,7 +114,7 @@ void fs_mkfile(const char *name) { | |||
114 | } | 114 | } |
115 | 115 | ||
116 | // create file | 116 | // create file |
117 | touch_file_as_user(expanded, getuid(), getgid(), 0600); | 117 | touch_file_as_user(expanded, 0600); |
118 | 118 | ||
119 | doexit: | 119 | doexit: |
120 | free(expanded); | 120 | free(expanded); |
diff --git a/src/firejail/fs_var.c b/src/firejail/fs_var.c index 9fbbdfa8f..8c53e6161 100644 --- a/src/firejail/fs_var.c +++ b/src/firejail/fs_var.c | |||
@@ -255,23 +255,8 @@ void fs_var_lock(void) { | |||
255 | fs_logger("tmpfs /var/lock"); | 255 | fs_logger("tmpfs /var/lock"); |
256 | } | 256 | } |
257 | else { | 257 | else { |
258 | char *lnk = realpath("/var/lock", NULL); | 258 | fwarning("/var/lock not mounted\n"); |
259 | if (lnk) { | 259 | dbg_test_dir("/var/lock"); |
260 | if (!is_dir(lnk)) { | ||
261 | // create directory | ||
262 | mkdir_attr(lnk, S_IRWXU|S_IRWXG|S_IRWXO, 0, 0); | ||
263 | } | ||
264 | if (arg_debug) | ||
265 | printf("Mounting tmpfs on %s on behalf of /var/lock\n", lnk); | ||
266 | if (mount("tmpfs", lnk, "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) | ||
267 | errExit("mounting /var/lock"); | ||
268 | free(lnk); | ||
269 | fs_logger("tmpfs /var/lock"); | ||
270 | } | ||
271 | else { | ||
272 | fwarning("/var/lock not mounted\n"); | ||
273 | dbg_test_dir("/var/lock"); | ||
274 | } | ||
275 | } | 260 | } |
276 | } | 261 | } |
277 | 262 | ||
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index c3d34e259..602985b4e 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -395,19 +395,8 @@ void fs_whitelist(void) { | |||
395 | new_name = expand_home(dataptr, cfg.homedir); | 395 | new_name = expand_home(dataptr, cfg.homedir); |
396 | assert(new_name); | 396 | assert(new_name); |
397 | 397 | ||
398 | // trim trailing slashes or dots | 398 | // remove trailing slashes and single dots |
399 | char *end = strchr(new_name, '\0'); | 399 | trim_trailing_slash_or_dot(new_name); |
400 | assert(end); | ||
401 | if ((end - new_name) > 1) { | ||
402 | end--; | ||
403 | while (*end == '/' || | ||
404 | (*end == '.' && *(end - 1) == '/')) { | ||
405 | *end = '\0'; | ||
406 | end--; | ||
407 | if (end == new_name) | ||
408 | break; | ||
409 | } | ||
410 | } | ||
411 | 400 | ||
412 | if (arg_debug || arg_debug_whitelists) | 401 | if (arg_debug || arg_debug_whitelists) |
413 | fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist"); | 402 | fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist"); |
diff --git a/src/firejail/join.c b/src/firejail/join.c index 729c7f797..cdd95b6a8 100644 --- a/src/firejail/join.c +++ b/src/firejail/join.c | |||
@@ -212,8 +212,10 @@ static void extract_umask(pid_t pid) { | |||
212 | 212 | ||
213 | FILE *fp = fopen(fname, "re"); | 213 | FILE *fp = fopen(fname, "re"); |
214 | free(fname); | 214 | free(fname); |
215 | if (!fp) | 215 | if (!fp) { |
216 | return; | 216 | fprintf(stderr, "Error: cannot open umask file\n"); |
217 | exit(1); | ||
218 | } | ||
217 | if (fscanf(fp, "%3o", &orig_umask) < 1) { | 219 | if (fscanf(fp, "%3o", &orig_umask) < 1) { |
218 | fprintf(stderr, "Error: cannot read umask\n"); | 220 | fprintf(stderr, "Error: cannot read umask\n"); |
219 | exit(1); | 221 | exit(1); |
@@ -221,6 +223,36 @@ static void extract_umask(pid_t pid) { | |||
221 | fclose(fp); | 223 | fclose(fp); |
222 | } | 224 | } |
223 | 225 | ||
226 | pid_t switch_to_child(pid_t pid) { | ||
227 | EUID_ROOT(); | ||
228 | errno = 0; | ||
229 | char *comm = pid_proc_comm(pid); | ||
230 | if (!comm) { | ||
231 | if (errno == ENOENT) { | ||
232 | fprintf(stderr, "Error: cannot find process with id %d\n", pid); | ||
233 | exit(1); | ||
234 | } | ||
235 | else { | ||
236 | fprintf(stderr, "Error: cannot read /proc file\n"); | ||
237 | exit(1); | ||
238 | } | ||
239 | } | ||
240 | EUID_USER(); | ||
241 | if (strcmp(comm, "firejail") == 0) { | ||
242 | pid_t child; | ||
243 | if (find_child(pid, &child) == 1) { | ||
244 | fprintf(stderr, "Error: no valid sandbox\n"); | ||
245 | exit(1); | ||
246 | } | ||
247 | fmessage("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) child); | ||
248 | pid = child; | ||
249 | } | ||
250 | free(comm); | ||
251 | return pid; | ||
252 | } | ||
253 | |||
254 | |||
255 | |||
224 | void join(pid_t pid, int argc, char **argv, int index) { | 256 | void join(pid_t pid, int argc, char **argv, int index) { |
225 | EUID_ASSERT(); | 257 | EUID_ASSERT(); |
226 | char *homedir = cfg.homedir; | 258 | char *homedir = cfg.homedir; |
@@ -229,19 +261,13 @@ void join(pid_t pid, int argc, char **argv, int index) { | |||
229 | extract_command(argc, argv, index); | 261 | extract_command(argc, argv, index); |
230 | signal (SIGTERM, signal_handler); | 262 | signal (SIGTERM, signal_handler); |
231 | 263 | ||
232 | // if the pid is that of a firejail process, use the pid of the first child process | 264 | // in case the pid is that of a firejail process, use the pid of the first child process |
233 | EUID_ROOT(); | 265 | pid = switch_to_child(pid); |
234 | char *comm = pid_proc_comm(pid); | 266 | |
235 | EUID_USER(); | 267 | // now check if the pid belongs to a firejail sandbox |
236 | if (comm) { | 268 | if (invalid_sandbox(pid)) { |
237 | if (strcmp(comm, "firejail") == 0) { | 269 | fprintf(stderr, "Error: no valid sandbox\n"); |
238 | pid_t child; | 270 | exit(1); |
239 | if (find_child(pid, &child) == 0) { | ||
240 | pid = child; | ||
241 | fmessage("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid); | ||
242 | } | ||
243 | } | ||
244 | free(comm); | ||
245 | } | 271 | } |
246 | 272 | ||
247 | // check privileges for non-root users | 273 | // check privileges for non-root users |
@@ -406,7 +432,7 @@ void join(pid_t pid, int argc, char **argv, int index) { | |||
406 | } | 432 | } |
407 | 433 | ||
408 | drop_privs(arg_nogroups); | 434 | drop_privs(arg_nogroups); |
409 | start_application(0); | 435 | start_application(0, NULL); |
410 | 436 | ||
411 | // it will never get here!!! | 437 | // it will never get here!!! |
412 | } | 438 | } |
diff --git a/src/firejail/main.c b/src/firejail/main.c index 4212edd9b..b3664ee2e 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -1652,7 +1652,7 @@ int main(int argc, char **argv) { | |||
1652 | else if (strncmp(argv[i], "--private-srv=", 14) == 0) { | 1652 | else if (strncmp(argv[i], "--private-srv=", 14) == 0) { |
1653 | // extract private srv list | 1653 | // extract private srv list |
1654 | if (*(argv[i] + 14) == '\0') { | 1654 | if (*(argv[i] + 14) == '\0') { |
1655 | fprintf(stderr, "Error: invalid private-etc option\n"); | 1655 | fprintf(stderr, "Error: invalid private-srv option\n"); |
1656 | exit(1); | 1656 | exit(1); |
1657 | } | 1657 | } |
1658 | if (cfg.srv_private_keep) { | 1658 | if (cfg.srv_private_keep) { |
diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c index de446d032..8fbd11bba 100644 --- a/src/firejail/netfilter.c +++ b/src/firejail/netfilter.c | |||
@@ -170,7 +170,12 @@ void netfilter_print(pid_t pid, int ipv6) { | |||
170 | 170 | ||
171 | // join the network namespace | 171 | // join the network namespace |
172 | pid_t child; | 172 | pid_t child; |
173 | if (find_child(pid, &child) == -1) { | 173 | if (find_child(pid, &child) == 1) { |
174 | fprintf(stderr, "Error: cannot join the network namespace\n"); | ||
175 | exit(1); | ||
176 | } | ||
177 | |||
178 | if (invalid_sandbox(child)) { | ||
174 | fprintf(stderr, "Error: cannot join the network namespace\n"); | 179 | fprintf(stderr, "Error: cannot join the network namespace\n"); |
175 | exit(1); | 180 | exit(1); |
176 | } | 181 | } |
diff --git a/src/firejail/network.txt b/src/firejail/network.txt deleted file mode 100644 index 75bdc346d..000000000 --- a/src/firejail/network.txt +++ /dev/null | |||
@@ -1,95 +0,0 @@ | |||
1 | struct Bridge { | ||
2 | char *dev; // bridge device name | ||
3 | uint32_t ip; // bridge device IP address | ||
4 | uint32_t mask; // bridge device mask | ||
5 | uint32_t ipsandbox // sandbox interface IP address | ||
6 | } | ||
7 | |||
8 | net_configure_bridge(br, device) { | ||
9 | br->dev = devname; | ||
10 | br->ip = extracted from kernel device - using net_get_if_addr() in network.c | ||
11 | br->mask = extracted from kernel device - using net_get_if_addr() in network.c | ||
12 | check available network range; /31 networks are not supported | ||
13 | } | ||
14 | |||
15 | net_configure_sandbox_ip(br) { | ||
16 | if br->ip_sandbox | ||
17 | check br->ipsandbox inside the bridge network | ||
18 | arp_check(br->ipsandbox) // send an arp req to check if anybody else is using this address | ||
19 | else | ||
20 | br->ipsandbox = arp_assign(); | ||
21 | } | ||
22 | |||
23 | net_configure_veth_pair { | ||
24 | create a veth pair | ||
25 | place one interface end in the bridge | ||
26 | place the other end in the namespace of the child process | ||
27 | } | ||
28 | |||
29 | net_bridge_wait_ip { | ||
30 | arp_check br->ipsandbox address to come up | ||
31 | wait for not more than 5 seconds | ||
32 | } | ||
33 | |||
34 | main() { | ||
35 | |||
36 | foreach argv[i] { | ||
37 | if --net | ||
38 | br = next bridge available | ||
39 | net_configure_bridge(br, device name from argv[i]); | ||
40 | else if --ip | ||
41 | br = last bridge configured | ||
42 | br->ipsandbox = ip address extracted from argv[i] | ||
43 | else if --defaultgw | ||
44 | cfg.defaultgw = ip address extracted from argv[i] | ||
45 | } | ||
46 | |||
47 | net_check_cfg(); // check the validity of network configuration so far | ||
48 | |||
49 | if (any bridge configured) { | ||
50 | lock /var/lock/firejail.lock file | ||
51 | for each bridge | ||
52 | net_configure_sandbox_ip(br) | ||
53 | } | ||
54 | |||
55 | clone (new network namespace if any bridge configured or --net=none) | ||
56 | |||
57 | if (any bridge configured) { | ||
58 | for each bridge | ||
59 | net_configure_veth_pair | ||
60 | } | ||
61 | |||
62 | notify child init is done | ||
63 | |||
64 | if (any bridge configured) { | ||
65 | for each bridge | ||
66 | net_bridge_wait_ip | ||
67 | unlock /var/lock/firejail.lock file | ||
68 | } | ||
69 | |||
70 | wait on child | ||
71 | exit | ||
72 | } | ||
73 | |||
74 | |||
75 | ****************************************************** | ||
76 | * macvlan notes | ||
77 | ****************************************************** | ||
78 | Configure a macvlan interface | ||
79 | |||
80 | # ip link add virtual0 link eth0 type macvlan mode bridge | ||
81 | (you can configure it with # ifconfig virtual0 192.168.1.52/24 up) | ||
82 | |||
83 | Create a new network namespace and move the interface in the new network namespace | ||
84 | |||
85 | # ip netns add dummy0 | ||
86 | # ip link set virtual0 netns dummy0 | ||
87 | |||
88 | Join the namespace and configure the interfaces | ||
89 | |||
90 | # ip netns exec dummy0 bash | ||
91 | # ifconfig lo up | ||
92 | # ifconfig virtual0 192.168.1.52/24 | ||
93 | |||
94 | Investigate ipvlan interface - added to linux kernel 3.19 | ||
95 | https://github.com/torvalds/linux/blob/master/Documentation/networking/ipvlan.txt | ||
diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c index e30d07229..e3c750767 100644 --- a/src/firejail/network_main.c +++ b/src/firejail/network_main.c | |||
@@ -269,18 +269,23 @@ void net_dns_print(pid_t pid) { | |||
269 | EUID_ASSERT(); | 269 | EUID_ASSERT(); |
270 | // drop privileges - will not be able to read /etc/resolv.conf for --noroot option | 270 | // drop privileges - will not be able to read /etc/resolv.conf for --noroot option |
271 | 271 | ||
272 | // if the pid is that of a firejail process, use the pid of the first child process | 272 | // in case the pid is that of a firejail process, use the pid of the first child process |
273 | EUID_ROOT(); | 273 | pid = switch_to_child(pid); |
274 | char *comm = pid_proc_comm(pid); | 274 | |
275 | EUID_USER(); | 275 | // now check if the pid belongs to a firejail sandbox |
276 | if (comm) { | 276 | if (invalid_sandbox(pid)) { |
277 | if (strcmp(comm, "firejail") == 0) { | 277 | fprintf(stderr, "Error: no valid sandbox\n"); |
278 | pid_t child; | 278 | exit(1); |
279 | if (find_child(pid, &child) == 0) { | 279 | } |
280 | pid = child; | 280 | |
281 | } | 281 | // check privileges for non-root users |
282 | uid_t uid = getuid(); | ||
283 | if (uid != 0) { | ||
284 | uid_t sandbox_uid = pid_get_uid(pid); | ||
285 | if (uid != sandbox_uid) { | ||
286 | fprintf(stderr, "Error: permission denied.\n"); | ||
287 | exit(1); | ||
282 | } | 288 | } |
283 | free(comm); | ||
284 | } | 289 | } |
285 | 290 | ||
286 | EUID_ROOT(); | 291 | EUID_ROOT(); |
diff --git a/src/firejail/no_sandbox.c b/src/firejail/no_sandbox.c index 5bd3f7e09..7c5cc1df9 100644 --- a/src/firejail/no_sandbox.c +++ b/src/firejail/no_sandbox.c | |||
@@ -233,5 +233,5 @@ void run_no_sandbox(int argc, char **argv) { | |||
233 | 233 | ||
234 | arg_quiet = 1; | 234 | arg_quiet = 1; |
235 | 235 | ||
236 | start_application(1); | 236 | start_application(1, NULL); |
237 | } | 237 | } |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 79fc36fb5..ea069de76 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -1050,7 +1050,6 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
1050 | 1050 | ||
1051 | // filesystem bind | 1051 | // filesystem bind |
1052 | if (strncmp(ptr, "bind ", 5) == 0) { | 1052 | if (strncmp(ptr, "bind ", 5) == 0) { |
1053 | #ifdef HAVE_BIND | ||
1054 | if (checkcfg(CFG_BIND)) { | 1053 | if (checkcfg(CFG_BIND)) { |
1055 | if (getuid() != 0) { | 1054 | if (getuid() != 0) { |
1056 | fprintf(stderr, "Error: --bind option is available only if running as root\n"); | 1055 | fprintf(stderr, "Error: --bind option is available only if running as root\n"); |
@@ -1083,7 +1082,6 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
1083 | } | 1082 | } |
1084 | else | 1083 | else |
1085 | warning_feature_disabled("bind"); | 1084 | warning_feature_disabled("bind"); |
1086 | #endif | ||
1087 | return 0; | 1085 | return 0; |
1088 | } | 1086 | } |
1089 | 1087 | ||
diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index eb3763253..9989ddb68 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c | |||
@@ -64,18 +64,13 @@ void protocol_print_filter(pid_t pid) { | |||
64 | 64 | ||
65 | (void) pid; | 65 | (void) pid; |
66 | #ifdef SYS_socket | 66 | #ifdef SYS_socket |
67 | // if the pid is that of a firejail process, use the pid of the first child process | 67 | // in case the pid is that of a firejail process, use the pid of the first child process |
68 | EUID_ROOT(); | 68 | pid = switch_to_child(pid); |
69 | char *comm = pid_proc_comm(pid); | 69 | |
70 | EUID_USER(); | 70 | // now check if the pid belongs to a firejail sandbox |
71 | if (comm) { | 71 | if (invalid_sandbox(pid)) { |
72 | if (strcmp(comm, "firejail") == 0) { | 72 | fprintf(stderr, "Error: no valid sandbox\n"); |
73 | pid_t child; | 73 | exit(1); |
74 | if (find_child(pid, &child) == 0) { | ||
75 | pid = child; | ||
76 | } | ||
77 | } | ||
78 | free(comm); | ||
79 | } | 74 | } |
80 | 75 | ||
81 | // check privileges for non-root users | 76 | // check privileges for non-root users |
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c index 521f144e8..e6696ecb4 100644 --- a/src/firejail/pulseaudio.c +++ b/src/firejail/pulseaudio.c | |||
@@ -117,7 +117,7 @@ void pulseaudio_init(void) { | |||
117 | 117 | ||
118 | int rv = mkdir(dir1, 0755); | 118 | int rv = mkdir(dir1, 0755); |
119 | if (rv == 0) { | 119 | if (rv == 0) { |
120 | if (set_perms(dir1, getuid(), getgid(), 0755)) | 120 | if (chmod(dir1, 0755)) |
121 | {;} // do nothing | 121 | {;} // do nothing |
122 | } | 122 | } |
123 | #ifdef HAVE_GCOV | 123 | #ifdef HAVE_GCOV |
@@ -153,7 +153,7 @@ void pulseaudio_init(void) { | |||
153 | 153 | ||
154 | int rv = mkdir(dir1, 0700); | 154 | int rv = mkdir(dir1, 0700); |
155 | if (rv == 0) { | 155 | if (rv == 0) { |
156 | if (set_perms(dir1, getuid(), getgid(), 0700)) | 156 | if (chmod(dir1, 0700)) |
157 | {;} // do nothing | 157 | {;} // do nothing |
158 | } | 158 | } |
159 | #ifdef HAVE_GCOV | 159 | #ifdef HAVE_GCOV |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index b0a792277..919a2b84e 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -139,6 +139,18 @@ void save_umask(void) { | |||
139 | } | 139 | } |
140 | } | 140 | } |
141 | 141 | ||
142 | static FILE *create_ready_for_join_file(void) { | ||
143 | FILE *fp = fopen(RUN_READY_FOR_JOIN, "wxe"); | ||
144 | if (fp) { | ||
145 | ASSERT_PERMS_STREAM(fp, 0, 0, 0644); | ||
146 | return fp; | ||
147 | } | ||
148 | else { | ||
149 | fprintf(stderr, "Error: cannot create %s\n", RUN_READY_FOR_JOIN); | ||
150 | exit(1); | ||
151 | } | ||
152 | } | ||
153 | |||
142 | static void sandbox_if_up(Bridge *br) { | 154 | static void sandbox_if_up(Bridge *br) { |
143 | assert(br); | 155 | assert(br); |
144 | if (!br->configured) | 156 | if (!br->configured) |
@@ -374,7 +386,7 @@ static int ok_to_run(const char *program) { | |||
374 | return 0; | 386 | return 0; |
375 | } | 387 | } |
376 | 388 | ||
377 | void start_application(int no_sandbox) { | 389 | void start_application(int no_sandbox, FILE *fp) { |
378 | // set environment | 390 | // set environment |
379 | if (no_sandbox == 0) { | 391 | if (no_sandbox == 0) { |
380 | env_defaults(); | 392 | env_defaults(); |
@@ -394,6 +406,11 @@ void start_application(int no_sandbox) { | |||
394 | #ifndef LTS | 406 | #ifndef LTS |
395 | if (arg_audit) { | 407 | if (arg_audit) { |
396 | assert(arg_audit_prog); | 408 | assert(arg_audit_prog); |
409 | |||
410 | if (fp) { | ||
411 | fprintf(fp, "ready\n"); | ||
412 | fclose(fp); | ||
413 | } | ||
397 | #ifdef HAVE_GCOV | 414 | #ifdef HAVE_GCOV |
398 | __gcov_dump(); | 415 | __gcov_dump(); |
399 | #endif | 416 | #endif |
@@ -426,6 +443,11 @@ void start_application(int no_sandbox) { | |||
426 | print_time(); | 443 | print_time(); |
427 | 444 | ||
428 | int rv = ok_to_run(cfg.original_argv[cfg.original_program_index]); | 445 | int rv = ok_to_run(cfg.original_argv[cfg.original_program_index]); |
446 | |||
447 | if (fp) { | ||
448 | fprintf(fp, "ready\n"); | ||
449 | fclose(fp); | ||
450 | } | ||
429 | #ifdef HAVE_GCOV | 451 | #ifdef HAVE_GCOV |
430 | __gcov_dump(); | 452 | __gcov_dump(); |
431 | #endif | 453 | #endif |
@@ -482,6 +504,11 @@ void start_application(int no_sandbox) { | |||
482 | 504 | ||
483 | if (!arg_command && !arg_quiet) | 505 | if (!arg_command && !arg_quiet) |
484 | print_time(); | 506 | print_time(); |
507 | |||
508 | if (fp) { | ||
509 | fprintf(fp, "ready\n"); | ||
510 | fclose(fp); | ||
511 | } | ||
485 | #ifdef HAVE_GCOV | 512 | #ifdef HAVE_GCOV |
486 | __gcov_dump(); | 513 | __gcov_dump(); |
487 | #endif | 514 | #endif |
@@ -1080,6 +1107,13 @@ int sandbox(void* sandbox_arg) { | |||
1080 | #endif | 1107 | #endif |
1081 | 1108 | ||
1082 | //**************************************** | 1109 | //**************************************** |
1110 | // communicate progress of sandbox set up | ||
1111 | // to --join | ||
1112 | //**************************************** | ||
1113 | |||
1114 | FILE *fp = create_ready_for_join_file(); | ||
1115 | |||
1116 | //**************************************** | ||
1083 | // create a new user namespace | 1117 | // create a new user namespace |
1084 | // - too early to drop privileges | 1118 | // - too early to drop privileges |
1085 | //**************************************** | 1119 | //**************************************** |
@@ -1144,9 +1178,11 @@ int sandbox(void* sandbox_arg) { | |||
1144 | #endif | 1178 | #endif |
1145 | 1179 | ||
1146 | prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died | 1180 | prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died |
1147 | start_application(0); // start app | 1181 | start_application(0, fp); // start app |
1148 | } | 1182 | } |
1149 | 1183 | ||
1184 | fclose(fp); | ||
1185 | |||
1150 | int status = monitor_application(app_pid); // monitor application | 1186 | int status = monitor_application(app_pid); // monitor application |
1151 | flush_stdin(); | 1187 | flush_stdin(); |
1152 | 1188 | ||
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 3da0206e1..7be7b3950 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c | |||
@@ -295,18 +295,13 @@ int seccomp_filter_keep(void) { | |||
295 | void seccomp_print_filter(pid_t pid) { | 295 | void seccomp_print_filter(pid_t pid) { |
296 | EUID_ASSERT(); | 296 | EUID_ASSERT(); |
297 | 297 | ||
298 | // if the pid is that of a firejail process, use the pid of the first child process | 298 | // in case the pid is that of a firejail process, use the pid of the first child process |
299 | EUID_ROOT(); | 299 | pid = switch_to_child(pid); |
300 | char *comm = pid_proc_comm(pid); | 300 | |
301 | EUID_USER(); | 301 | // now check if the pid belongs to a firejail sandbox |
302 | if (comm) { | 302 | if (invalid_sandbox(pid)) { |
303 | if (strcmp(comm, "firejail") == 0) { | 303 | fprintf(stderr, "Error: no valid sandbox\n"); |
304 | pid_t child; | 304 | exit(1); |
305 | if (find_child(pid, &child) == 0) { | ||
306 | pid = child; | ||
307 | } | ||
308 | } | ||
309 | free(comm); | ||
310 | } | 305 | } |
311 | 306 | ||
312 | // check privileges for non-root users | 307 | // check privileges for non-root users |
diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 78cd30926..c8866da3a 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c | |||
@@ -37,10 +37,8 @@ static char *usage_str = | |||
37 | #ifdef HAVE_NETWORK | 37 | #ifdef HAVE_NETWORK |
38 | " --bandwidth=name|pid - set bandwidth limits.\n" | 38 | " --bandwidth=name|pid - set bandwidth limits.\n" |
39 | #endif | 39 | #endif |
40 | #ifdef HAVE_BIND | ||
41 | " --bind=dirname1,dirname2 - mount-bind dirname1 on top of dirname2.\n" | 40 | " --bind=dirname1,dirname2 - mount-bind dirname1 on top of dirname2.\n" |
42 | " --bind=filename1,filename2 - mount-bind filename1 on top of filename2.\n" | 41 | " --bind=filename1,filename2 - mount-bind filename1 on top of filename2.\n" |
43 | #endif | ||
44 | " --blacklist=filename - blacklist directory or file.\n" | 42 | " --blacklist=filename - blacklist directory or file.\n" |
45 | " --build - build a whitelisted profile for the application.\n" | 43 | " --build - build a whitelisted profile for the application.\n" |
46 | " --build=filename - build a whitelisted profile for the application.\n" | 44 | " --build=filename - build a whitelisted profile for the application.\n" |
@@ -153,6 +151,7 @@ static char *usage_str = | |||
153 | " --overlay-clean - clean all overlays stored in $HOME/.firejail directory.\n" | 151 | " --overlay-clean - clean all overlays stored in $HOME/.firejail directory.\n" |
154 | " --private - temporary home directory.\n" | 152 | " --private - temporary home directory.\n" |
155 | " --private=directory - use directory as user home.\n" | 153 | " --private=directory - use directory as user home.\n" |
154 | " --private-cache - temporary ~/.cache directory.\n" | ||
156 | " --private-home=file,directory - build a new user home in a temporary\n" | 155 | " --private-home=file,directory - build a new user home in a temporary\n" |
157 | "\tfilesystem, and copy the files and directories in the list in\n" | 156 | "\tfilesystem, and copy the files and directories in the list in\n" |
158 | "\tthe new home.\n" | 157 | "\tthe new home.\n" |
diff --git a/src/firejail/util.c b/src/firejail/util.c index 329ae141b..050f7534a 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -156,7 +156,6 @@ int mkpath_as_root(const char* path) { | |||
156 | *p='\0'; | 156 | *p='\0'; |
157 | if (mkdir(file_path, 0755)==-1) { | 157 | if (mkdir(file_path, 0755)==-1) { |
158 | if (errno != EEXIST) { | 158 | if (errno != EEXIST) { |
159 | *p='/'; | ||
160 | free(file_path); | 159 | free(file_path); |
161 | return -1; | 160 | return -1; |
162 | } | 161 | } |
@@ -365,7 +364,7 @@ void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_ | |||
365 | } | 364 | } |
366 | 365 | ||
367 | // return -1 if error, 0 if no error | 366 | // return -1 if error, 0 if no error |
368 | void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode) { | 367 | void touch_file_as_user(const char *fname, mode_t mode) { |
369 | pid_t child = fork(); | 368 | pid_t child = fork(); |
370 | if (child < 0) | 369 | if (child < 0) |
371 | errExit("fork"); | 370 | errExit("fork"); |
@@ -373,10 +372,10 @@ void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode) { | |||
373 | // drop privileges | 372 | // drop privileges |
374 | drop_privs(0); | 373 | drop_privs(0); |
375 | 374 | ||
376 | FILE *fp = fopen(fname, "w"); | 375 | FILE *fp = fopen(fname, "wx"); |
377 | if (fp) { | 376 | if (fp) { |
378 | fprintf(fp, "\n"); | 377 | fprintf(fp, "\n"); |
379 | SET_PERMS_STREAM(fp, uid, gid, mode); | 378 | SET_PERMS_STREAM(fp, -1, -1, mode); |
380 | fclose(fp); | 379 | fclose(fp); |
381 | } | 380 | } |
382 | #ifdef HAVE_GCOV | 381 | #ifdef HAVE_GCOV |
@@ -425,15 +424,48 @@ int is_link(const char *fname) { | |||
425 | if (*fname == '\0') | 424 | if (*fname == '\0') |
426 | return 0; | 425 | return 0; |
427 | 426 | ||
427 | char *dup = NULL; | ||
428 | struct stat s; | 428 | struct stat s; |
429 | if (lstat(fname, &s) == 0) { | 429 | if (lstat(fname, &s) == 0) { |
430 | if (S_ISLNK(s.st_mode)) | 430 | if (S_ISLNK(s.st_mode)) |
431 | return 1; | 431 | return 1; |
432 | if (S_ISDIR(s.st_mode)) { | ||
433 | // remove trailing slashes and single dots and try again | ||
434 | dup = strdup(fname); | ||
435 | if (!dup) | ||
436 | errExit("strdup"); | ||
437 | trim_trailing_slash_or_dot(dup); | ||
438 | if (lstat(dup, &s) == 0) { | ||
439 | if (S_ISLNK(s.st_mode)) { | ||
440 | free(dup); | ||
441 | return 1; | ||
442 | } | ||
443 | } | ||
444 | } | ||
432 | } | 445 | } |
433 | 446 | ||
447 | free(dup); | ||
434 | return 0; | 448 | return 0; |
435 | } | 449 | } |
436 | 450 | ||
451 | // remove all slashes and single dots from the end of a path | ||
452 | // for example /foo/bar///././. -> /foo/bar | ||
453 | void trim_trailing_slash_or_dot(char *path) { | ||
454 | assert(path); | ||
455 | |||
456 | char *end = strchr(path, '\0'); | ||
457 | assert(end); | ||
458 | if ((end - path) > 1) { | ||
459 | end--; | ||
460 | while (*end == '/' || | ||
461 | (*end == '.' && *(end - 1) == '/')) { | ||
462 | *end = '\0'; | ||
463 | end--; | ||
464 | if (end == path) | ||
465 | break; | ||
466 | } | ||
467 | } | ||
468 | } | ||
437 | 469 | ||
438 | // remove multiple spaces and return allocated memory | 470 | // remove multiple spaces and return allocated memory |
439 | char *line_remove_spaces(const char *buf) { | 471 | char *line_remove_spaces(const char *buf) { |
@@ -762,12 +794,14 @@ uid_t pid_get_uid(pid_t pid) { | |||
762 | char buf[PIDS_BUFLEN]; | 794 | char buf[PIDS_BUFLEN]; |
763 | while (fgets(buf, PIDS_BUFLEN - 1, fp)) { | 795 | while (fgets(buf, PIDS_BUFLEN - 1, fp)) { |
764 | if (strncmp(buf, "Uid:", 4) == 0) { | 796 | if (strncmp(buf, "Uid:", 4) == 0) { |
765 | char *ptr = buf + 5; | 797 | char *ptr = buf + 4; |
766 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { | 798 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { |
767 | ptr++; | 799 | ptr++; |
768 | } | 800 | } |
769 | if (*ptr == '\0') | 801 | if (*ptr == '\0') { |
770 | break; | 802 | fprintf(stderr, "Error: cannot read /proc file\n"); |
803 | exit(1); | ||
804 | } | ||
771 | 805 | ||
772 | rv = atoi(ptr); | 806 | rv = atoi(ptr); |
773 | break; // break regardless! | 807 | break; // break regardless! |
@@ -778,10 +812,6 @@ uid_t pid_get_uid(pid_t pid) { | |||
778 | free(file); | 812 | free(file); |
779 | EUID_USER(); // grsecurity fix | 813 | EUID_USER(); // grsecurity fix |
780 | 814 | ||
781 | if (rv == 0) { | ||
782 | fprintf(stderr, "Error: cannot read /proc file\n"); | ||
783 | exit(1); | ||
784 | } | ||
785 | return rv; | 815 | return rv; |
786 | } | 816 | } |
787 | 817 | ||
@@ -891,10 +921,8 @@ void create_empty_file_as_root(const char *fname, mode_t mode) { | |||
891 | FILE *fp = fopen(fname, "w"); | 921 | FILE *fp = fopen(fname, "w"); |
892 | if (!fp) | 922 | if (!fp) |
893 | errExit("fopen"); | 923 | errExit("fopen"); |
894 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR); | 924 | SET_PERMS_STREAM(fp, 0, 0, mode); |
895 | fclose(fp); | 925 | fclose(fp); |
896 | if (chmod(fname, mode) == -1) | ||
897 | errExit("chmod"); | ||
898 | } | 926 | } |
899 | } | 927 | } |
900 | 928 | ||
@@ -1022,7 +1050,7 @@ int safe_fd(const char *path, int flags) { | |||
1022 | errExit("open"); | 1050 | errExit("open"); |
1023 | 1051 | ||
1024 | // traverse the path and return -1 if a symlink is encountered | 1052 | // traverse the path and return -1 if a symlink is encountered |
1025 | int entered = 0; | 1053 | int weird_pathname = 1; |
1026 | int fd = -1; | 1054 | int fd = -1; |
1027 | char *tok = strtok(dup, "/"); | 1055 | char *tok = strtok(dup, "/"); |
1028 | while (tok) { | 1056 | while (tok) { |
@@ -1031,7 +1059,7 @@ int safe_fd(const char *path, int flags) { | |||
1031 | tok = strtok(NULL, "/"); | 1059 | tok = strtok(NULL, "/"); |
1032 | continue; | 1060 | continue; |
1033 | } | 1061 | } |
1034 | entered = 1; | 1062 | weird_pathname = 0; |
1035 | 1063 | ||
1036 | // open the directory | 1064 | // open the directory |
1037 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 1065 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
@@ -1046,7 +1074,7 @@ int safe_fd(const char *path, int flags) { | |||
1046 | } | 1074 | } |
1047 | if (p != dup) { | 1075 | if (p != dup) { |
1048 | // consistent flags for top level directories (////foo, /.///foo) | 1076 | // consistent flags for top level directories (////foo, /.///foo) |
1049 | if (!entered) | 1077 | if (weird_pathname) |
1050 | flags = O_PATH|O_DIRECTORY|O_CLOEXEC; | 1078 | flags = O_PATH|O_DIRECTORY|O_CLOEXEC; |
1051 | // open last path segment | 1079 | // open last path segment |
1052 | fd = openat(parentfd, p + 1, flags|O_NOFOLLOW); | 1080 | fd = openat(parentfd, p + 1, flags|O_NOFOLLOW); |
@@ -1059,3 +1087,66 @@ errexit: | |||
1059 | fprintf(stderr, "Error: cannot open \"%s\", invalid filename\n", path); | 1087 | fprintf(stderr, "Error: cannot open \"%s\", invalid filename\n", path); |
1060 | exit(1); | 1088 | exit(1); |
1061 | } | 1089 | } |
1090 | |||
1091 | |||
1092 | // return 1 if the sandbox identified by pid is not fully set up yet or if | ||
1093 | // it is no firejail sandbox at all, return 0 if the sandbox is complete | ||
1094 | int invalid_sandbox(const pid_t pid) { | ||
1095 | // check if a file "ready-for-join" exists | ||
1096 | char *fname; | ||
1097 | if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_READY_FOR_JOIN) == -1) | ||
1098 | errExit("asprintf"); | ||
1099 | EUID_ROOT(); | ||
1100 | FILE *fp = fopen(fname, "re"); | ||
1101 | EUID_USER(); | ||
1102 | free(fname); | ||
1103 | if (!fp) | ||
1104 | return 1; | ||
1105 | // regular file owned by root | ||
1106 | int fd = fileno(fp); | ||
1107 | if (fd == -1) | ||
1108 | errExit("fileno"); | ||
1109 | struct stat s; | ||
1110 | if (fstat(fd, &s) == -1) | ||
1111 | errExit("fstat"); | ||
1112 | if (!S_ISREG(s.st_mode) || s.st_uid != 0) { | ||
1113 | fclose(fp); | ||
1114 | return 1; | ||
1115 | } | ||
1116 | // check if it is non-empty | ||
1117 | char buf[BUFLEN]; | ||
1118 | if (fgets(buf, BUFLEN, fp) == NULL) { | ||
1119 | fclose(fp); | ||
1120 | return 1; | ||
1121 | } | ||
1122 | fclose(fp); | ||
1123 | // confirm "ready" string was written | ||
1124 | if (strncmp(buf, "ready\n", 6) != 0) | ||
1125 | return 1; | ||
1126 | |||
1127 | // walk down the process tree a few nodes, there should be no firejail leaf | ||
1128 | #define MAXNODES 5 | ||
1129 | pid_t current = pid, next; | ||
1130 | int i; | ||
1131 | for (i = 0; i < MAXNODES; i++) { | ||
1132 | if (find_child(current, &next) == 1) { | ||
1133 | // found a leaf | ||
1134 | EUID_ROOT(); | ||
1135 | char *comm = pid_proc_comm(current); | ||
1136 | EUID_USER(); | ||
1137 | if (!comm) { | ||
1138 | fprintf(stderr, "Error: cannot read /proc file\n"); | ||
1139 | exit(1); | ||
1140 | } | ||
1141 | if (strcmp(comm, "firejail") == 0) { | ||
1142 | free(comm); | ||
1143 | return 1; | ||
1144 | } | ||
1145 | free(comm); | ||
1146 | break; | ||
1147 | } | ||
1148 | current = next; | ||
1149 | } | ||
1150 | |||
1151 | return 0; | ||
1152 | } | ||
diff --git a/src/lib/common.c b/src/lib/common.c index fa988446b..d6dd43c4b 100644 --- a/src/lib/common.c +++ b/src/lib/common.c | |||
@@ -129,7 +129,7 @@ char *pid_proc_comm(const pid_t pid) { | |||
129 | // open /proc/pid/cmdline file | 129 | // open /proc/pid/cmdline file |
130 | char *fname; | 130 | char *fname; |
131 | int fd; | 131 | int fd; |
132 | if (asprintf(&fname, "/proc/%d//comm", pid) == -1) | 132 | if (asprintf(&fname, "/proc/%d/comm", pid) == -1) |
133 | return NULL; | 133 | return NULL; |
134 | if ((fd = open(fname, O_RDONLY)) < 0) { | 134 | if ((fd = open(fname, O_RDONLY)) < 0) { |
135 | free(fname); | 135 | free(fname); |
@@ -154,6 +154,8 @@ char *pid_proc_comm(const pid_t pid) { | |||
154 | 154 | ||
155 | // return a malloc copy of the command line | 155 | // return a malloc copy of the command line |
156 | char *rv = strdup(buffer); | 156 | char *rv = strdup(buffer); |
157 | if (!rv) | ||
158 | return NULL; | ||
157 | if (strlen(rv) == 0) { | 159 | if (strlen(rv) == 0) { |
158 | free(rv); | 160 | free(rv); |
159 | return NULL; | 161 | return NULL; |
@@ -192,6 +194,8 @@ char *pid_proc_cmdline(const pid_t pid) { | |||
192 | 194 | ||
193 | // return a malloc copy of the command line | 195 | // return a malloc copy of the command line |
194 | char *rv = strdup((char *) buffer); | 196 | char *rv = strdup((char *) buffer); |
197 | if (!rv) | ||
198 | return NULL; | ||
195 | if (strlen(rv) == 0) { | 199 | if (strlen(rv) == 0) { |
196 | free(rv); | 200 | free(rv); |
197 | return NULL; | 201 | return NULL; |
diff --git a/src/lib/pid.c b/src/lib/pid.c index 3c804716d..75576c787 100644 --- a/src/lib/pid.c +++ b/src/lib/pid.c | |||
@@ -149,7 +149,7 @@ uid_t pid_get_uid(pid_t pid) { | |||
149 | char buf[PIDS_BUFLEN]; | 149 | char buf[PIDS_BUFLEN]; |
150 | while (fgets(buf, PIDS_BUFLEN - 1, fp)) { | 150 | while (fgets(buf, PIDS_BUFLEN - 1, fp)) { |
151 | if (strncmp(buf, "Uid:", 4) == 0) { | 151 | if (strncmp(buf, "Uid:", 4) == 0) { |
152 | char *ptr = buf + 5; | 152 | char *ptr = buf + 4; |
153 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { | 153 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { |
154 | ptr++; | 154 | ptr++; |
155 | } | 155 | } |
@@ -398,7 +398,7 @@ void pid_read(pid_t mon_pid) { | |||
398 | pids[pid].parent = parent; | 398 | pids[pid].parent = parent; |
399 | } | 399 | } |
400 | else if (strncmp(buf, "Uid:", 4) == 0) { | 400 | else if (strncmp(buf, "Uid:", 4) == 0) { |
401 | char *ptr = buf + 5; | 401 | char *ptr = buf + 4; |
402 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { | 402 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { |
403 | ptr++; | 403 | ptr++; |
404 | } | 404 | } |
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index e29cf4f4b..17562c503 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt | |||
@@ -113,6 +113,8 @@ Example: "nowhitelist ~/.config" | |||
113 | Ignore command. | 113 | Ignore command. |
114 | 114 | ||
115 | Example: "ignore seccomp" | 115 | Example: "ignore seccomp" |
116 | .br | ||
117 | Example: "ignore net ehh0" | ||
116 | 118 | ||
117 | .TP | 119 | .TP |
118 | \fBquiet | 120 | \fBquiet |
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index f29d9cddf..7de1bff50 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -170,7 +170,7 @@ in order to allow strace to run. Chromium and Chromium-based browsers will not w | |||
170 | .br | 170 | .br |
171 | Example: | 171 | Example: |
172 | .br | 172 | .br |
173 | $ firejail --build=profile-file vlc ~/Videos/test.mp4 | 173 | $ firejail --build vlc ~/Videos/test.mp4 |
174 | .TP | 174 | .TP |
175 | \fB\-\-build=profile-file | 175 | \fB\-\-build=profile-file |
176 | The command builds a whitelisted profile, and saves it in profile-file. If /usr/bin/strace is installed on the system, it also | 176 | The command builds a whitelisted profile, and saves it in profile-file. If /usr/bin/strace is installed on the system, it also |
@@ -509,7 +509,8 @@ Ignore command in profile file. | |||
509 | Example: | 509 | Example: |
510 | .br | 510 | .br |
511 | $ firejail \-\-ignore=shell --ignore=seccomp firefox | 511 | $ firejail \-\-ignore=shell --ignore=seccomp firefox |
512 | 512 | .br | |
513 | $ firejail \-\-ignore="net eth0" firefox | ||
513 | .TP | 514 | .TP |
514 | \fB\-\-interface=interface | 515 | \fB\-\-interface=interface |
515 | Move interface in a new network namespace. Up to four --interface options can be specified. | 516 | Move interface in a new network namespace. Up to four --interface options can be specified. |
diff --git a/src/man/firemon.txt b/src/man/firemon.txt index 9cae72b54..214fcac44 100644 --- a/src/man/firemon.txt +++ b/src/man/firemon.txt | |||
@@ -105,7 +105,7 @@ The owner of the sandbox. | |||
105 | .SH LICENSE | 105 | .SH LICENSE |
106 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. | 106 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
107 | .PP | 107 | .PP |
108 | Homepage: http://firejail.wordpress.com | 108 | Homepage: https://firejail.wordpress.com |
109 | .SH SEE ALSO | 109 | .SH SEE ALSO |
110 | \&\flfirejail\fR\|(1), | 110 | \&\flfirejail\fR\|(1), |
111 | \&\flfirecfg\fR\|(1), | 111 | \&\flfirecfg\fR\|(1), |