diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firecfg/firecfg.config | 8 | ||||
-rw-r--r-- | src/firejail/firejail.h | 4 | ||||
-rw-r--r-- | src/firejail/fs.c | 204 | ||||
-rw-r--r-- | src/firejail/profile.c | 5 | ||||
-rw-r--r-- | src/man/firejail-profile.txt | 2 | ||||
-rw-r--r-- | src/profstats/Makefile.in | 14 | ||||
-rw-r--r-- | src/profstats/main.c | 240 |
7 files changed, 396 insertions, 81 deletions
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config index 4cd4fad6c..2798605d5 100644 --- a/src/firecfg/firecfg.config +++ b/src/firecfg/firecfg.config | |||
@@ -23,6 +23,7 @@ Natron | |||
23 | PPSSPPQt | 23 | PPSSPPQt |
24 | QMediathekView | 24 | QMediathekView |
25 | QOwnNotes | 25 | QOwnNotes |
26 | Screenshot | ||
26 | Telegram | 27 | Telegram |
27 | Viber | 28 | Viber |
28 | VirtualBox | 29 | VirtualBox |
@@ -148,6 +149,7 @@ desktopeditors | |||
148 | devhelp | 149 | devhelp |
149 | dex2jar | 150 | dex2jar |
150 | dia | 151 | dia |
152 | dig | ||
151 | digikam | 153 | digikam |
152 | dillo | 154 | dillo |
153 | dino | 155 | dino |
@@ -275,6 +277,7 @@ gnome-passwordsafe | |||
275 | gnome-photos | 277 | gnome-photos |
276 | gnome-recipes | 278 | gnome-recipes |
277 | gnome-schedule | 279 | gnome-schedule |
280 | gnome-screenshot | ||
278 | gnome-system-log | 281 | gnome-system-log |
279 | gnome-twitch | 282 | gnome-twitch |
280 | gnome-weather | 283 | gnome-weather |
@@ -303,6 +306,7 @@ hashcat | |||
303 | hedgewars | 306 | hedgewars |
304 | hexchat | 307 | hexchat |
305 | highlight | 308 | highlight |
309 | host | ||
306 | hugin | 310 | hugin |
307 | icecat | 311 | icecat |
308 | icedove | 312 | icedove |
@@ -466,6 +470,7 @@ nitroshare-nmh | |||
466 | nitroshare-send | 470 | nitroshare-send |
467 | nitroshare-ui | 471 | nitroshare-ui |
468 | nomacs | 472 | nomacs |
473 | nslookup | ||
469 | nylas | 474 | nylas |
470 | nyx | 475 | nyx |
471 | obs | 476 | obs |
@@ -479,6 +484,7 @@ ooviewdoc | |||
479 | open-invaders | 484 | open-invaders |
480 | openarena | 485 | openarena |
481 | opencity | 486 | opencity |
487 | openclonk | ||
482 | openoffice.org | 488 | openoffice.org |
483 | openshot | 489 | openshot |
484 | openshot-qt | 490 | openshot-qt |
@@ -546,6 +552,7 @@ rhythmbox-client | |||
546 | ricochet | 552 | ricochet |
547 | riot-desktop | 553 | riot-desktop |
548 | riot-web | 554 | riot-web |
555 | ripperx | ||
549 | ristretto | 556 | ristretto |
550 | rocketchat | 557 | rocketchat |
551 | rtorrent | 558 | rtorrent |
@@ -578,6 +585,7 @@ smtube | |||
578 | snox | 585 | snox |
579 | soffice | 586 | soffice |
580 | sol | 587 | sol |
588 | sound-juicer | ||
581 | soundconverter | 589 | soundconverter |
582 | spotify | 590 | spotify |
583 | sqlitebrowser | 591 | sqlitebrowser |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 0e4fcea6a..7391a8994 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -395,6 +395,7 @@ typedef enum { | |||
395 | MOUNT_TMPFS, | 395 | MOUNT_TMPFS, |
396 | MOUNT_NOEXEC, | 396 | MOUNT_NOEXEC, |
397 | MOUNT_RDWR, | 397 | MOUNT_RDWR, |
398 | MOUNT_RDWR_NOCHECK, // no check of ownership | ||
398 | OPERATION_MAX | 399 | OPERATION_MAX |
399 | } OPERATION; | 400 | } OPERATION; |
400 | 401 | ||
@@ -403,8 +404,7 @@ void fs_blacklist(void); | |||
403 | // mount a writable tmpfs | 404 | // mount a writable tmpfs |
404 | void fs_tmpfs(const char *dir, unsigned check_owner); | 405 | void fs_tmpfs(const char *dir, unsigned check_owner); |
405 | // remount noexec/nodev/nosuid or read-only or read-write | 406 | // remount noexec/nodev/nosuid or read-only or read-write |
406 | void fs_remount(const char *dir, OPERATION op, unsigned check_mnt); | 407 | void fs_remount(const char *dir, OPERATION op, int rec); |
407 | void fs_remount_rec(const char *dir, OPERATION op, unsigned check_mnt); | ||
408 | // mount /proc and /sys directories | 408 | // mount /proc and /sys directories |
409 | void fs_proc_sys_dev_boot(void); | 409 | void fs_proc_sys_dev_boot(void); |
410 | // blacklist firejail configuration and runtime directories | 410 | // blacklist firejail configuration and runtime directories |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index c7dd91b06..b642329bf 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -28,10 +28,9 @@ | |||
28 | #include <dirent.h> | 28 | #include <dirent.h> |
29 | #include <errno.h> | 29 | #include <errno.h> |
30 | 30 | ||
31 | |||
32 | #include <fcntl.h> | 31 | #include <fcntl.h> |
33 | #ifndef O_PATH | 32 | #ifndef O_PATH |
34 | # define O_PATH 010000000 | 33 | #define O_PATH 010000000 |
35 | #endif | 34 | #endif |
36 | 35 | ||
37 | #define MAX_BUF 4096 | 36 | #define MAX_BUF 4096 |
@@ -43,6 +42,8 @@ | |||
43 | //*********************************************** | 42 | //*********************************************** |
44 | // process profile file | 43 | // process profile file |
45 | //*********************************************** | 44 | //*********************************************** |
45 | static void fs_remount_rec(const char *dir, OPERATION op); | ||
46 | |||
46 | static char *opstr[] = { | 47 | static char *opstr[] = { |
47 | [BLACKLIST_FILE] = "blacklist", | 48 | [BLACKLIST_FILE] = "blacklist", |
48 | [BLACKLIST_NOLOG] = "blacklist-nolog", | 49 | [BLACKLIST_NOLOG] = "blacklist-nolog", |
@@ -50,6 +51,7 @@ static char *opstr[] = { | |||
50 | [MOUNT_TMPFS] = "tmpfs", | 51 | [MOUNT_TMPFS] = "tmpfs", |
51 | [MOUNT_NOEXEC] = "noexec", | 52 | [MOUNT_NOEXEC] = "noexec", |
52 | [MOUNT_RDWR] = "read-write", | 53 | [MOUNT_RDWR] = "read-write", |
54 | [MOUNT_RDWR_NOCHECK] = "read-write", | ||
53 | }; | 55 | }; |
54 | 56 | ||
55 | typedef enum { | 57 | typedef enum { |
@@ -148,7 +150,7 @@ static void disable_file(OPERATION op, const char *filename) { | |||
148 | } | 150 | } |
149 | } | 151 | } |
150 | else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { | 152 | else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { |
151 | fs_remount_rec(fname, op, 1); | 153 | fs_remount_rec(fname, op); |
152 | // todo: last_disable = SUCCESSFUL; | 154 | // todo: last_disable = SUCCESSFUL; |
153 | } | 155 | } |
154 | else if (op == MOUNT_TMPFS) { | 156 | else if (op == MOUNT_TMPFS) { |
@@ -425,21 +427,11 @@ void fs_blacklist(void) { | |||
425 | free(noblacklist); | 427 | free(noblacklist); |
426 | } | 428 | } |
427 | 429 | ||
428 | static int get_mount_flags(const char *path, unsigned long *flags) { | ||
429 | struct statvfs buf; | ||
430 | |||
431 | if (statvfs(path, &buf) < 0) | ||
432 | return -errno; | ||
433 | *flags = buf.f_flag; | ||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | //*********************************************** | 430 | //*********************************************** |
438 | // mount namespace | 431 | // mount namespace |
439 | // - functions need fully resolved paths | ||
440 | //*********************************************** | 432 | //*********************************************** |
441 | 433 | ||
442 | // mount a writable tmpfs on directory | 434 | // mount a writable tmpfs on directory; requires a resolved path |
443 | void fs_tmpfs(const char *dir, unsigned check_owner) { | 435 | void fs_tmpfs(const char *dir, unsigned check_owner) { |
444 | assert(dir); | 436 | assert(dir); |
445 | if (arg_debug) | 437 | if (arg_debug) |
@@ -480,71 +472,114 @@ void fs_tmpfs(const char *dir, unsigned check_owner) { | |||
480 | close(fd); | 472 | close(fd); |
481 | } | 473 | } |
482 | 474 | ||
483 | void fs_remount(const char *dir, OPERATION op, unsigned check_mnt) { | 475 | // remount path, but preserve existing mount flags; requires a resolved path |
484 | assert(dir); | 476 | static void fs_remount_simple(const char *path, OPERATION op) { |
485 | // check directory exists | 477 | assert(path); |
478 | |||
479 | // open path without following symbolic links | ||
480 | int fd = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | ||
481 | if (fd == -1) | ||
482 | goto out; | ||
483 | // identify file owner | ||
486 | struct stat s; | 484 | struct stat s; |
487 | int rv = stat(dir, &s); | 485 | if (fstat(fd, &s) == -1) { |
488 | if (rv == 0) { | 486 | // fstat can fail with EACCES if path is a FUSE mount, |
489 | unsigned long flags = 0; | 487 | // mounted without 'allow_root' or 'allow_other' |
490 | if (get_mount_flags(dir, &flags) != 0) { | 488 | if (errno != EACCES) |
491 | fwarning("cannot remount %s\n", dir); | 489 | errExit("fstat"); |
490 | close(fd); | ||
491 | goto out; | ||
492 | } | ||
493 | // get mount flags | ||
494 | struct statvfs buf; | ||
495 | if (fstatvfs(fd, &buf) == -1) | ||
496 | errExit("fstatvfs"); | ||
497 | unsigned long flags = buf.f_flag; | ||
498 | |||
499 | // read-write option | ||
500 | if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { | ||
501 | // nothing to do if there is no read-only flag | ||
502 | if ((flags & MS_RDONLY) == 0) { | ||
503 | close(fd); | ||
492 | return; | 504 | return; |
493 | } | 505 | } |
494 | if (op == MOUNT_RDWR) { | 506 | // allow only user owned directories, except the user is root |
495 | // allow only user owned directories, except the user is root | 507 | if (op == MOUNT_RDWR && getuid() != 0 && s.st_uid != getuid()) { |
496 | if (getuid() != 0 && s.st_uid != getuid()) { | 508 | fwarning("you are not allowed to change %s to read-write\n", path); |
497 | fwarning("you are not allowed to change %s to read-write\n", dir); | 509 | close(fd); |
498 | return; | 510 | return; |
499 | } | ||
500 | if ((flags & MS_RDONLY) == 0) | ||
501 | return; | ||
502 | flags &= ~MS_RDONLY; | ||
503 | } | ||
504 | else if (op == MOUNT_NOEXEC) { | ||
505 | if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) | ||
506 | return; | ||
507 | flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; | ||
508 | } | 511 | } |
509 | else if (op == MOUNT_READONLY) { | 512 | flags &= ~MS_RDONLY; |
510 | if ((flags & MS_RDONLY) == MS_RDONLY) | 513 | } |
511 | return; | 514 | // noexec option |
512 | flags |= MS_RDONLY; | 515 | else if (op == MOUNT_NOEXEC) { |
516 | // nothing to do if path is mounted noexec already | ||
517 | if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { | ||
518 | close(fd); | ||
519 | return; | ||
513 | } | 520 | } |
514 | else | 521 | flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; |
515 | assert(0); | 522 | } |
516 | 523 | // read-only option | |
517 | if (arg_debug) | 524 | else if (op == MOUNT_READONLY) { |
518 | printf("Mounting %s %s\n", opstr[op], dir); | 525 | // nothing to do if path is mounted read-only already |
519 | // mount --bind /bin /bin | 526 | if ((flags & MS_RDONLY) == MS_RDONLY) { |
520 | // mount --bind -o remount,rw /bin | 527 | close(fd); |
521 | if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || | 528 | return; |
522 | mount(NULL, dir, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) | ||
523 | errExit("remounting"); | ||
524 | // run a sanity check on /proc/self/mountinfo | ||
525 | if (check_mnt) { | ||
526 | // confirm target of the last mount operation was dir; if there are other | ||
527 | // mount points contained inside dir, one of those will show up as target | ||
528 | // of the last mount operation instead | ||
529 | MountData *mptr = get_last_mount(); | ||
530 | size_t len = strlen(dir); | ||
531 | if ((strncmp(mptr->dir, dir, len) != 0 || | ||
532 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) | ||
533 | && strcmp(dir, "/") != 0) // support read-only=/ | ||
534 | errLogExit("invalid %s mount", opstr[op]); | ||
535 | } | 529 | } |
536 | fs_logger2(opstr[op], dir); | 530 | flags |= MS_RDONLY; |
537 | } | 531 | } |
532 | else | ||
533 | assert(0); | ||
534 | |||
535 | if (arg_debug) | ||
536 | printf("Mounting %s %s\n", opstr[op], path); | ||
537 | // mount --bind /bin /bin | ||
538 | char *proc; | ||
539 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
540 | errExit("asprintf"); | ||
541 | if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
542 | errExit("mount"); | ||
543 | free(proc); | ||
544 | close(fd); | ||
545 | |||
546 | // mount --bind -o remount,ro /bin | ||
547 | // we need to open path again | ||
548 | fd = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | ||
549 | if (fd == -1) | ||
550 | errExit("open"); | ||
551 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
552 | errExit("asprintf"); | ||
553 | if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) | ||
554 | errExit("mount"); | ||
555 | |||
556 | // run a sanity check on /proc/self/mountinfo and confirm that target of the last | ||
557 | // mount operation was path; if there are other mount points contained inside path, | ||
558 | // one of those will show up as target of the last mount operation instead | ||
559 | MountData *mptr = get_last_mount(); | ||
560 | size_t len = strlen(path); | ||
561 | if ((strncmp(mptr->dir, path, len) != 0 || | ||
562 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) | ||
563 | && strcmp(path, "/") != 0) // support read-only=/ | ||
564 | errLogExit("invalid %s mount", opstr[op]); | ||
565 | fs_logger2(opstr[op], path); | ||
566 | free(proc); | ||
567 | close(fd); | ||
568 | return; | ||
569 | |||
570 | out: | ||
571 | fwarning("not remounting %s\n", path); | ||
538 | } | 572 | } |
539 | 573 | ||
540 | void fs_remount_rec(const char *dir, OPERATION op, unsigned check_mnt) { | 574 | // remount recursively; requires a resolved path |
575 | static void fs_remount_rec(const char *dir, OPERATION op) { | ||
541 | assert(dir); | 576 | assert(dir); |
542 | struct stat s; | 577 | struct stat s; |
543 | if (stat(dir, &s) != 0) | 578 | if (stat(dir, &s) != 0) |
544 | return; | 579 | return; |
545 | if (!S_ISDIR(s.st_mode)) { | 580 | if (!S_ISDIR(s.st_mode)) { |
546 | // no need to search in /proc/self/mountinfo for submounts if not a directory | 581 | // no need to search in /proc/self/mountinfo for submounts if not a directory |
547 | fs_remount(dir, op, check_mnt); | 582 | fs_remount_simple(dir, op); |
548 | return; | 583 | return; |
549 | } | 584 | } |
550 | // get mount point of the directory | 585 | // get mount point of the directory |
@@ -558,7 +593,7 @@ void fs_remount_rec(const char *dir, OPERATION op, unsigned check_mnt) { | |||
558 | fwarning("read-only, read-write and noexec options are not applied recursively\n"); | 593 | fwarning("read-only, read-write and noexec options are not applied recursively\n"); |
559 | mount_warning = 1; | 594 | mount_warning = 1; |
560 | } | 595 | } |
561 | fs_remount(dir, op, check_mnt); | 596 | fs_remount_simple(dir, op); |
562 | return; | 597 | return; |
563 | } | 598 | } |
564 | // build array with all mount points that need to get remounted | 599 | // build array with all mount points that need to get remounted |
@@ -567,12 +602,25 @@ void fs_remount_rec(const char *dir, OPERATION op, unsigned check_mnt) { | |||
567 | // remount | 602 | // remount |
568 | char **tmp = arr; | 603 | char **tmp = arr; |
569 | while (*tmp) { | 604 | while (*tmp) { |
570 | fs_remount(*tmp, op, check_mnt); | 605 | fs_remount_simple(*tmp, op); |
571 | free(*tmp++); | 606 | free(*tmp++); |
572 | } | 607 | } |
573 | free(arr); | 608 | free(arr); |
574 | } | 609 | } |
575 | 610 | ||
611 | // resolve a path and remount it | ||
612 | void fs_remount(const char *path, OPERATION op, int rec) { | ||
613 | assert(path); | ||
614 | char *rpath = realpath(path, NULL); | ||
615 | if (rpath) { | ||
616 | if (rec) | ||
617 | fs_remount_rec(rpath, op); | ||
618 | else | ||
619 | fs_remount_simple(rpath, op); | ||
620 | free(rpath); | ||
621 | } | ||
622 | } | ||
623 | |||
576 | // Disable /mnt, /media, /run/mount and /run/media access | 624 | // Disable /mnt, /media, /run/mount and /run/media access |
577 | void fs_mnt(const int enforce) { | 625 | void fs_mnt(const int enforce) { |
578 | if (enforce) { | 626 | if (enforce) { |
@@ -749,22 +797,22 @@ void fs_basic_fs(void) { | |||
749 | if (arg_debug) | 797 | if (arg_debug) |
750 | printf("Basic read-only filesystem:\n"); | 798 | printf("Basic read-only filesystem:\n"); |
751 | if (!arg_writable_etc) { | 799 | if (!arg_writable_etc) { |
752 | fs_remount("/etc", MOUNT_READONLY, 0); | 800 | fs_remount("/etc", MOUNT_READONLY, 1); |
753 | if (uid) | 801 | if (uid) |
754 | fs_remount("/etc", MOUNT_NOEXEC, 0); | 802 | fs_remount("/etc", MOUNT_NOEXEC, 1); |
755 | } | 803 | } |
756 | if (!arg_writable_var) { | 804 | if (!arg_writable_var) { |
757 | fs_remount("/var", MOUNT_READONLY, 0); | 805 | fs_remount("/var", MOUNT_READONLY, 1); |
758 | if (uid) | 806 | if (uid) |
759 | fs_remount("/var", MOUNT_NOEXEC, 0); | 807 | fs_remount("/var", MOUNT_NOEXEC, 1); |
760 | } | 808 | } |
761 | fs_remount("/bin", MOUNT_READONLY, 0); | 809 | fs_remount("/usr", MOUNT_READONLY, 1); |
762 | fs_remount("/sbin", MOUNT_READONLY, 0); | 810 | fs_remount("/bin", MOUNT_READONLY, 1); |
763 | fs_remount("/lib", MOUNT_READONLY, 0); | 811 | fs_remount("/sbin", MOUNT_READONLY, 1); |
764 | fs_remount("/lib64", MOUNT_READONLY, 0); | 812 | fs_remount("/lib", MOUNT_READONLY, 1); |
765 | fs_remount("/lib32", MOUNT_READONLY, 0); | 813 | fs_remount("/lib64", MOUNT_READONLY, 1); |
766 | fs_remount("/libx32", MOUNT_READONLY, 0); | 814 | fs_remount("/lib32", MOUNT_READONLY, 1); |
767 | fs_remount("/usr", MOUNT_READONLY, 0); | 815 | fs_remount("/libx32", MOUNT_READONLY, 1); |
768 | 816 | ||
769 | // update /var directory in order to support multiple sandboxes running on the same root directory | 817 | // update /var directory in order to support multiple sandboxes running on the same root directory |
770 | fs_var_lock(); | 818 | fs_var_lock(); |
@@ -773,7 +821,7 @@ void fs_basic_fs(void) { | |||
773 | if (!arg_writable_var_log) | 821 | if (!arg_writable_var_log) |
774 | fs_var_log(); | 822 | fs_var_log(); |
775 | else | 823 | else |
776 | fs_remount("/var/log", MOUNT_RDWR, 0); | 824 | fs_remount("/var/log", MOUNT_RDWR_NOCHECK, 0); |
777 | 825 | ||
778 | fs_var_lib(); | 826 | fs_var_lib(); |
779 | fs_var_cache(); | 827 | fs_var_cache(); |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 969209869..c7269857d 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -151,6 +151,10 @@ static int check_nodbus(void) { | |||
151 | return arg_nodbus != 0; | 151 | return arg_nodbus != 0; |
152 | } | 152 | } |
153 | 153 | ||
154 | static int check_nosound(void) { | ||
155 | return arg_nosound != 0; | ||
156 | } | ||
157 | |||
154 | static int check_x11(void) { | 158 | static int check_x11(void) { |
155 | return (arg_x11_block || arg_x11_xorg || getenv("FIREJAIL_X11")); | 159 | return (arg_x11_block || arg_x11_xorg || getenv("FIREJAIL_X11")); |
156 | } | 160 | } |
@@ -167,6 +171,7 @@ Cond conditionals[] = { | |||
167 | {"HAS_APPIMAGE", check_appimage}, | 171 | {"HAS_APPIMAGE", check_appimage}, |
168 | {"HAS_NET", check_netoptions}, | 172 | {"HAS_NET", check_netoptions}, |
169 | {"HAS_NODBUS", check_nodbus}, | 173 | {"HAS_NODBUS", check_nodbus}, |
174 | {"HAS_NOSOUND", check_nosound}, | ||
170 | {"HAS_X11", check_x11}, | 175 | {"HAS_X11", check_x11}, |
171 | {"BROWSER_DISABLE_U2F", check_disable_u2f}, | 176 | {"BROWSER_DISABLE_U2F", check_disable_u2f}, |
172 | {"BROWSER_ALLOW_DRM", check_allow_drm}, | 177 | {"BROWSER_ALLOW_DRM", check_allow_drm}, |
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index 84aed41a4..9af25bf63 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt | |||
@@ -103,7 +103,7 @@ Example: "?HAS_APPIMAGE: whitelist ${HOME}/special/appimage/dir" | |||
103 | 103 | ||
104 | This example will load the whitelist profile line only if the \-\-appimage option has been specified on the command line. | 104 | This example will load the whitelist profile line only if the \-\-appimage option has been specified on the command line. |
105 | 105 | ||
106 | Currently the only conditionals supported this way are HAS_APPIMAGE, HAS_NET, HAS_NODBUS and HAS_X11. The conditionals BROWSER_DISABLE_U2F and BROWSER_ALLOW_DRM | 106 | Currently the only conditionals supported this way are HAS_APPIMAGE, HAS_NET, HAS_NODBUS, HAS_NOSOUND and HAS_X11. The conditionals BROWSER_DISABLE_U2F and BROWSER_ALLOW_DRM |
107 | can be enabled or disabled globally in Firejail's configuration file. | 107 | can be enabled or disabled globally in Firejail's configuration file. |
108 | 108 | ||
109 | The profile line may be any profile line that you would normally use in a profile \fBexcept\fR for "quiet" and "include" lines. | 109 | The profile line may be any profile line that you would normally use in a profile \fBexcept\fR for "quiet" and "include" lines. |
diff --git a/src/profstats/Makefile.in b/src/profstats/Makefile.in new file mode 100644 index 000000000..4ada23c23 --- /dev/null +++ b/src/profstats/Makefile.in | |||
@@ -0,0 +1,14 @@ | |||
1 | all: ../../etc/profstats | ||
2 | |||
3 | include ../common.mk | ||
4 | |||
5 | %.o : %.c $(H_FILE_LIST) | ||
6 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ | ||
7 | |||
8 | ../../etc/profstats: $(OBJS) | ||
9 | $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) | ||
10 | |||
11 | clean:; rm -fr *.o ../../etc/profstats *.gcov *.gcda *.gcno *.plist | ||
12 | |||
13 | distclean: clean | ||
14 | rm -fr Makefile | ||
diff --git a/src/profstats/main.c b/src/profstats/main.c new file mode 100644 index 000000000..775142643 --- /dev/null +++ b/src/profstats/main.c | |||
@@ -0,0 +1,240 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2020 Firejail Authors | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | #include <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <string.h> | ||
23 | #include <assert.h> | ||
24 | |||
25 | #define MAXBUF 2048 | ||
26 | // stats | ||
27 | static int cnt_profiles = 0; | ||
28 | static int cnt_apparmor = 0; | ||
29 | static int cnt_seccomp = 0; | ||
30 | static int cnt_caps = 0; | ||
31 | static int cnt_dotlocal = 0; | ||
32 | static int cnt_globalsdotlocal = 0; | ||
33 | static int cnt_netnone = 0; | ||
34 | static int cnt_noexec = 0; // include disable-exec.inc | ||
35 | static int cnt_privatedev = 0; | ||
36 | static int cnt_privatetmp = 0; | ||
37 | static int cnt_whitelistvar = 0; // include whitelist-var-common.inc | ||
38 | static int cnt_ssh = 0; | ||
39 | |||
40 | static int level = 0; | ||
41 | static int arg_debug = 0; | ||
42 | static int arg_apparmor = 0; | ||
43 | static int arg_caps = 0; | ||
44 | static int arg_seccomp = 0; | ||
45 | static int arg_noexec = 0; | ||
46 | static int arg_privatedev = 0; | ||
47 | static int arg_privatetmp = 0; | ||
48 | static int arg_whitelistvar = 0; | ||
49 | static int arg_ssh = 0; | ||
50 | |||
51 | static void usage(void) { | ||
52 | printf("proftool - print profile statistics\n"); | ||
53 | printf("Usage: proftool [options] file[s]\n"); | ||
54 | printf("Options:\n"); | ||
55 | printf(" --apparmor - print profiles without apparmor\n"); | ||
56 | printf(" --caps - print profiles without caps\n"); | ||
57 | printf(" --ssh - print profiles without \"include disable-common.inc\"\n"); | ||
58 | printf(" --noexec - print profiles without \"include disable-exec.inc\"\n"); | ||
59 | printf(" --private-dev - print profiles without private-dev\n"); | ||
60 | printf(" --private-tmp - print profiles without private-tmp\n"); | ||
61 | printf(" --seccomp - print profiles without seccomp\n"); | ||
62 | printf(" --whitelist-var - print profiles without \"include whitelist-var-common.inc\"\n"); | ||
63 | printf(" --debug\n"); | ||
64 | printf("\n"); | ||
65 | } | ||
66 | |||
67 | void process_file(const char *fname) { | ||
68 | assert(fname); | ||
69 | |||
70 | if (arg_debug) | ||
71 | printf("processing #%s#\n", fname); | ||
72 | level++; | ||
73 | assert(level < 32); // to do - check in firejail code | ||
74 | |||
75 | FILE *fp = fopen(fname, "r"); | ||
76 | if (!fp) { | ||
77 | fprintf(stderr, "Error: cannot open %s\n", fname); | ||
78 | exit(1); | ||
79 | } | ||
80 | |||
81 | char buf[MAXBUF]; | ||
82 | while (fgets(buf, MAXBUF, fp)) { | ||
83 | char *ptr = strchr(buf, '\n'); | ||
84 | if (ptr) | ||
85 | *ptr = '\0'; | ||
86 | ptr = buf; | ||
87 | |||
88 | while (*ptr == ' ' || *ptr == '\t') | ||
89 | ptr++; | ||
90 | if (*ptr == '\n' || *ptr == '#') | ||
91 | continue; | ||
92 | |||
93 | if (strncmp(ptr, "seccomp", 7) == 0) | ||
94 | cnt_seccomp++; | ||
95 | else if (strncmp(ptr, "caps", 4) == 0) | ||
96 | cnt_caps++; | ||
97 | else if (strncmp(ptr, "include disable-exec.inc", 24) == 0) | ||
98 | cnt_noexec++; | ||
99 | else if (strncmp(ptr, "include whitelist-var-common.inc", 32) == 0) | ||
100 | cnt_whitelistvar++; | ||
101 | else if (strncmp(ptr, "include disable-common.inc", 26) == 0) | ||
102 | cnt_ssh++; | ||
103 | else if (strncmp(ptr, "net none", 8) == 0) | ||
104 | cnt_netnone++; | ||
105 | else if (strncmp(ptr, "apparmor", 8) == 0) | ||
106 | cnt_apparmor++; | ||
107 | else if (strncmp(ptr, "private-dev", 11) == 0) | ||
108 | cnt_privatedev++; | ||
109 | else if (strncmp(ptr, "private-tmp", 11) == 0) | ||
110 | cnt_privatetmp++; | ||
111 | else if (strncmp(ptr, "include ", 8) == 0) { | ||
112 | // not processing .local files | ||
113 | if (strstr(ptr, ".local")) { | ||
114 | //printf("dotlocal %d, level %d - #%s#, redirect #%s#\n", cnt_dotlocal, level, fname, buf + 8); | ||
115 | if (strstr(ptr, "globals.local")) | ||
116 | cnt_globalsdotlocal++; | ||
117 | else | ||
118 | cnt_dotlocal++; | ||
119 | continue; | ||
120 | } | ||
121 | process_file(buf + 8); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | fclose(fp); | ||
126 | level--; | ||
127 | } | ||
128 | |||
129 | int main(int argc, char **argv) { | ||
130 | if (argc <= 1) { | ||
131 | usage(); | ||
132 | return 1; | ||
133 | } | ||
134 | |||
135 | int start = 1; | ||
136 | int i; | ||
137 | for (i = 1; i < argc; i++) { | ||
138 | if (strcmp(argv[i], "--help") == 0) { | ||
139 | usage(); | ||
140 | return 0; | ||
141 | } | ||
142 | else if (strcmp(argv[i], "--debug") == 0) | ||
143 | arg_debug = 1; | ||
144 | else if (strcmp(argv[i], "--apparmor") == 0) | ||
145 | arg_apparmor = 1; | ||
146 | else if (strcmp(argv[i], "--caps") == 0) | ||
147 | arg_caps = 1; | ||
148 | else if (strcmp(argv[i], "--seccomp") == 0) | ||
149 | arg_seccomp = 1; | ||
150 | else if (strcmp(argv[i], "--noexec") == 0) | ||
151 | arg_noexec = 1; | ||
152 | else if (strcmp(argv[i], "--private-dev") == 0) | ||
153 | arg_privatedev = 1; | ||
154 | else if (strcmp(argv[i], "--private-tmp") == 0) | ||
155 | arg_privatetmp = 1; | ||
156 | else if (strcmp(argv[i], "--whitelist-var") == 0) | ||
157 | arg_whitelistvar = 1; | ||
158 | else if (strcmp(argv[i], "--ssh") == 0) | ||
159 | arg_ssh = 1; | ||
160 | else if (*argv[i] == '-') { | ||
161 | fprintf(stderr, "Error: invalid option %s\n", argv[i]); | ||
162 | return 1; | ||
163 | } | ||
164 | else | ||
165 | break; | ||
166 | } | ||
167 | |||
168 | start = i; | ||
169 | if (i == argc) { | ||
170 | fprintf(stderr, "Error: no porfile file specified\n"); | ||
171 | return 1; | ||
172 | } | ||
173 | |||
174 | for (i = start; i < argc; i++) { | ||
175 | cnt_profiles++; | ||
176 | |||
177 | // watch seccomp | ||
178 | int seccomp = cnt_seccomp; | ||
179 | int caps = cnt_caps; | ||
180 | int apparmor = cnt_apparmor; | ||
181 | int noexec = cnt_noexec; | ||
182 | int privatetmp = cnt_privatetmp; | ||
183 | int privatedev = cnt_privatedev; | ||
184 | int dotlocal = cnt_dotlocal; | ||
185 | int globalsdotlocal = cnt_globalsdotlocal; | ||
186 | int whitelistvar = cnt_whitelistvar; | ||
187 | int ssh = cnt_ssh; | ||
188 | |||
189 | // process file | ||
190 | process_file(argv[i]); | ||
191 | |||
192 | // warnings | ||
193 | if ((caps + 2) <= cnt_caps) { | ||
194 | printf("Warning: multiple caps in %s\n", argv[i]); | ||
195 | cnt_caps = caps + 1; | ||
196 | } | ||
197 | |||
198 | // fix redirections | ||
199 | if (cnt_dotlocal > (dotlocal + 1)) | ||
200 | cnt_dotlocal = dotlocal + 1; | ||
201 | if (cnt_globalsdotlocal > (globalsdotlocal + 1)) | ||
202 | cnt_globalsdotlocal = globalsdotlocal + 1; | ||
203 | |||
204 | if (arg_apparmor && apparmor == cnt_apparmor) | ||
205 | printf("No apparmor found in %s\n", argv[i]); | ||
206 | if (arg_caps && caps == cnt_caps) | ||
207 | printf("No caps found in %s\n", argv[i]); | ||
208 | if (arg_seccomp && seccomp == cnt_seccomp) | ||
209 | printf("No seccomp found in %s\n", argv[i]); | ||
210 | if (arg_noexec && noexec == cnt_noexec) | ||
211 | printf("No include disable-exec.inc found in %s\n", argv[i]); | ||
212 | if (arg_privatedev && privatedev == cnt_privatedev) | ||
213 | printf("No private-dev found in %s\n", argv[i]); | ||
214 | if (arg_privatetmp && privatetmp == cnt_privatetmp) | ||
215 | printf("No private-tmp found in %s\n", argv[i]); | ||
216 | if (arg_whitelistvar && whitelistvar == cnt_whitelistvar) | ||
217 | printf("No include whitelist-var-common.inc found in %s\n", argv[i]); | ||
218 | if (arg_ssh && ssh == cnt_ssh) | ||
219 | printf("No include disable-common.inc found in %s\n", argv[i]); | ||
220 | |||
221 | assert(level == 0); | ||
222 | } | ||
223 | |||
224 | printf("\n"); | ||
225 | printf("Stats:\n"); | ||
226 | printf(" profiles\t\t\t%d\n", cnt_profiles); | ||
227 | printf(" include local profile\t%d (include profile-name.local)\n", cnt_dotlocal); | ||
228 | printf(" include globals\t\t%d (include globals.local)\n", cnt_dotlocal); | ||
229 | printf(" blacklist ~/.ssh\t\t%d (include disable-common.inc)\n", cnt_ssh); | ||
230 | printf(" seccomp\t\t\t%d\n", cnt_seccomp); | ||
231 | printf(" capabilities\t\t%d\n", cnt_caps); | ||
232 | printf(" noexec\t\t\t%d (include disable-exec.inc)\n", cnt_noexec); | ||
233 | printf(" apparmor\t\t\t%d\n", cnt_apparmor); | ||
234 | printf(" private-dev\t\t\t%d\n", cnt_privatedev); | ||
235 | printf(" private-tmp\t\t\t%d\n", cnt_privatetmp); | ||
236 | printf(" whitelist var directory\t%d (include whitelist-var-common.inc)\n", cnt_whitelistvar); | ||
237 | printf(" net none\t\t\t%d\n", cnt_netnone); | ||
238 | printf("\n"); | ||
239 | return 0; | ||
240 | } \ No newline at end of file | ||