aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/firecfg/firecfg.config2
-rw-r--r--src/firecfg/main.c181
-rw-r--r--src/firejail/arg-checking.txt84
-rw-r--r--src/firejail/bandwidth.c7
-rw-r--r--src/firejail/caps.c19
-rw-r--r--src/firejail/cpu.c19
-rw-r--r--src/firejail/firejail.h8
-rw-r--r--src/firejail/fs.c47
-rw-r--r--src/firejail/fs_etc.c8
-rw-r--r--src/firejail/fs_home.c81
-rw-r--r--src/firejail/fs_logger.c17
-rw-r--r--src/firejail/fs_mkdir.c2
-rw-r--r--src/firejail/fs_var.c19
-rw-r--r--src/firejail/fs_whitelist.c15
-rw-r--r--src/firejail/join.c58
-rw-r--r--src/firejail/main.c2
-rw-r--r--src/firejail/netfilter.c7
-rw-r--r--src/firejail/network.txt95
-rw-r--r--src/firejail/network_main.c27
-rw-r--r--src/firejail/no_sandbox.c2
-rw-r--r--src/firejail/profile.c2
-rw-r--r--src/firejail/protocol.c19
-rw-r--r--src/firejail/pulseaudio.c4
-rw-r--r--src/firejail/sandbox.c40
-rw-r--r--src/firejail/seccomp.c19
-rw-r--r--src/firejail/usage.c3
-rw-r--r--src/firejail/util.c125
-rw-r--r--src/lib/common.c6
-rw-r--r--src/lib/pid.c4
-rw-r--r--src/man/firejail-profile.txt2
-rw-r--r--src/man/firejail.txt5
-rw-r--r--src/man/firemon.txt2
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
77clamdscan 77clamdscan
78clamdtop 78clamdtop
79clamscan 79clamscan
80clamtk
80claws-mail 81claws-mail
81clementine 82clementine
82clipit 83clipit
@@ -328,6 +329,7 @@ pluma
328polari 329polari
329ppsspp 330ppsspp
330psi-plus 331psi-plus
332pybitmessage
331# pycharm-community - FB note: may enable later 333# pycharm-community - FB note: may enable later
332# pycharm-professional 334# pycharm-professional
333qbittorrent 335qbittorrent
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"
23int arg_debug = 0; 23int arg_debug = 0;
24char *arg_bindir = "/usr/local/bin";
24 25
25static char *usage_str = 26static 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
64static void list(void) { 66static 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
99static void clean(void) { 101static 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
282static 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
295static 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
310errexit:
311 fprintf(stderr, "Error: cannot find home directory for user %s\n", user);
312 exit(1);
313}
278 314
279int main(int argc, char **argv) { 315int 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
416errexit: 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 @@
1arg checking:
2
31. --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
122. --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
213. --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
284. --tmpfs=dirname
29 - supported in profiles
30 - checking string chars
31 - checking no ".."
32 - unit test
33
345. --blacklist=filename, --blacklist=dirname
35 - supported in profiles
36 - checking string chars
37 - checking no ".."
38 - unit test
39
406. --read-only=filename, --read-only=dirname
41 - supported in profiles
42 - checking string chars
43 - checking no ".."
44 - unit test
45
467. --profile=filename
47 - check access as real GID/UID
48 - checking no dir
49 - checking no link
50 - checking no ".."
51 - unit test
52
538. --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
629. --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
7010. --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
7811. --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:
401void caps_print_filter(pid_t pid) { 401void 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) {
165void cpu_print_filter(pid_t pid) { 165void 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
407int sandbox(void* sandbox_arg); 408int sandbox(void* sandbox_arg);
408void start_application(int no_sandbox); 409void start_application(int no_sandbox, FILE *fp);
409 410
410// network_main.c 411// network_main.c
411void net_configure_sandbox_ip(Bridge *br); 412void net_configure_sandbox_ip(Bridge *br);
@@ -477,6 +478,7 @@ void usage(void);
477 478
478// join.c 479// join.c
479void join(pid_t pid, int argc, char **argv, int index); 480void join(pid_t pid, int argc, char **argv, int index);
481pid_t switch_to_child(pid_t pid);
480 482
481// shutdown.c 483// shutdown.c
482void shut(pid_t pid); 484void shut(pid_t pid);
@@ -512,9 +514,10 @@ void logerr(const char *msg);
512int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 514int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
513void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 515void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
514void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 516void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
515void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode); 517void touch_file_as_user(const char *fname, mode_t mode);
516int is_dir(const char *fname); 518int is_dir(const char *fname);
517int is_link(const char *fname); 519int is_link(const char *fname);
520void trim_trailing_slash_or_dot(char *path);
518char *line_remove_spaces(const char *buf); 521char *line_remove_spaces(const char *buf);
519char *split_comma(char *str); 522char *split_comma(char *str);
520void check_unsigned(const char *str, const char *msg); 523void check_unsigned(const char *str, const char *msg);
@@ -536,6 +539,7 @@ unsigned extract_timeout(const char *str);
536void disable_file_or_dir(const char *fname); 539void disable_file_or_dir(const char *fname);
537void disable_file_path(const char *path, const char *file); 540void disable_file_path(const char *path, const char *file);
538int safe_fd(const char *path, int flags); 541int safe_fd(const char *path, int flags);
542int 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
102static void duplicate(const char *fname, const char *private_dir, const char *private_run_dir) { 102static 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
373static char *check_dir_or_file(const char *name) { 373static 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
428errexit:
429 fprintf(stderr, "Error: invalid file %s\n", name);
430 exit(1);
432} 431}
433 432
434static void duplicate(char *name) { 433static 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) {
120void fs_logger_print_log(pid_t pid) { 120void 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
119doexit: 119doexit:
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
226pid_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
224void join(pid_t pid, int argc, char **argv, int index) { 256void 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 @@
1struct 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
8net_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
15net_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
23net_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
29net_bridge_wait_ip {
30 arp_check br->ipsandbox address to come up
31 wait for not more than 5 seconds
32}
33
34main() {
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******************************************************
78Configure 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
83Create 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
88Join 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
94Investigate ipvlan interface - added to linux kernel 3.19
95https://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
142static 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
142static void sandbox_if_up(Bridge *br) { 154static 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
377void start_application(int no_sandbox) { 389void 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) {
295void seccomp_print_filter(pid_t pid) { 295void 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
368void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode) { 367void 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
453void 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
439char *line_remove_spaces(const char *buf) { 471char *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
1094int 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"
113Ignore command. 113Ignore command.
114 114
115Example: "ignore seccomp" 115Example: "ignore seccomp"
116.br
117Example: "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
171Example: 171Example:
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
176The command builds a whitelisted profile, and saves it in profile-file. If /usr/bin/strace is installed on the system, it also 176The 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.
509Example: 509Example:
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
515Move interface in a new network namespace. Up to four --interface options can be specified. 516Move 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
106This 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. 106This 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
108Homepage: http://firejail.wordpress.com 108Homepage: 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),