From ba231088e6bd8e4c52e372f7a4d2928ee7bf223a Mon Sep 17 00:00:00 2001 From: netblue30 Date: Sun, 22 Oct 2017 11:09:50 -0400 Subject: --build: save the resulting profile in a file --- src/fbuilder/build_bin.c | 10 +++--- src/fbuilder/build_fs.c | 52 +++++++++++++++---------------- src/fbuilder/build_home.c | 8 ++--- src/fbuilder/build_profile.c | 74 ++++++++++++++++++++++---------------------- src/fbuilder/build_seccomp.c | 47 ++++++++++++++-------------- src/fbuilder/fbuilder.h | 22 ++++++------- src/fbuilder/filedb.c | 4 +-- src/fbuilder/main.c | 26 ++++++++++++++-- src/firejail/main.c | 32 ++++++++++++------- src/firejail/usage.c | 1 + src/man/firejail.txt | 16 ++++++++-- 11 files changed, 168 insertions(+), 124 deletions(-) (limited to 'src') diff --git a/src/fbuilder/build_bin.c b/src/fbuilder/build_bin.c index 6c173bfcc..fb92fb630 100644 --- a/src/fbuilder/build_bin.c +++ b/src/fbuilder/build_bin.c @@ -95,7 +95,7 @@ static void process_bin(const char *fname) { // process fname, fname.1, fname.2, fname.3, fname.4, fname.5 -void build_bin(const char *fname) { +void build_bin(const char *fname, FILE *fp) { assert(fname); // run fname @@ -114,13 +114,13 @@ void build_bin(const char *fname) { } if (bin_out) { - printf("private-bin "); + fprintf(fp, "private-bin "); FileDB *ptr = bin_out; while (ptr) { - printf("%s,", ptr->fname); + fprintf(fp, "%s,", ptr->fname); ptr = ptr->next; } - printf("\n"); - printf("# private-lib\n"); + fprintf(fp, "\n"); + fprintf(fp, "# private-lib\n"); } } diff --git a/src/fbuilder/build_fs.c b/src/fbuilder/build_fs.c index 01104edb1..f1a27a35a 100644 --- a/src/fbuilder/build_fs.c +++ b/src/fbuilder/build_fs.c @@ -125,21 +125,21 @@ static void etc_callback(char *ptr) { etc_out = filedb_add(etc_out, ptr); } -void build_etc(const char *fname) { +void build_etc(const char *fname, FILE *fp) { assert(fname); process_files(fname, "/etc", etc_callback); - printf("private-etc "); + fprintf(fp, "private-etc "); if (etc_out == NULL) - printf("none\n"); + fprintf(fp, "none\n"); else { FileDB *ptr = etc_out; while (ptr) { - printf("%s,", ptr->fname); + fprintf(fp, "%s,", ptr->fname); ptr = ptr->next; } - printf("\n"); + fprintf(fp, "\n"); } } @@ -160,15 +160,15 @@ static void var_callback(char *ptr) { var_out = filedb_add(var_out, ptr); } -void build_var(const char *fname) { +void build_var(const char *fname, FILE *fp) { assert(fname); process_files(fname, "/var", var_callback); if (var_out == NULL) - printf("blacklist /var\n"); + fprintf(fp, "blacklist /var\n"); else - filedb_print(var_out, "whitelist "); + filedb_print(var_out, "whitelist ", fp); } @@ -197,15 +197,15 @@ static void share_callback(char *ptr) { share_out = filedb_add(share_out, ptr); } -void build_share(const char *fname) { +void build_share(const char *fname, FILE *fp) { assert(fname); process_files(fname, "/usr/share", share_callback); if (share_out == NULL) - printf("blacklist /usr/share\n"); + fprintf(fp, "blacklist /usr/share\n"); else - filedb_print(share_out, "whitelist "); + filedb_print(share_out, "whitelist ", fp); } //******************************************* @@ -216,21 +216,21 @@ static void tmp_callback(char *ptr) { filedb_add(tmp_out, ptr); } -void build_tmp(const char *fname) { +void build_tmp(const char *fname, FILE *fp) { assert(fname); process_files(fname, "/tmp", tmp_callback); if (tmp_out == NULL) - printf("private-tmp\n"); + fprintf(fp, "private-tmp\n"); else { - printf("\n"); - printf("# private-tmp\n"); - printf("# File accessed in /tmp directory:\n"); - printf("# "); + fprintf(fp, "\n"); + fprintf(fp, "# private-tmp\n"); + fprintf(fp, "# File accessed in /tmp directory:\n"); + fprintf(fp, "# "); FileDB *ptr = tmp_out; while (ptr) { - printf("%s,", ptr->fname); + fprintf(fp, "%s,", ptr->fname); ptr = ptr->next; } printf("\n"); @@ -294,24 +294,24 @@ static void dev_callback(char *ptr) { filedb_add(dev_out, ptr); } -void build_dev(const char *fname) { +void build_dev(const char *fname, FILE *fp) { assert(fname); process_files(fname, "/dev", dev_callback); if (dev_out == NULL) - printf("private-dev\n"); + fprintf(fp, "private-dev\n"); else { - printf("\n"); - printf("# private-dev\n"); - printf("# This is the list of devices accessed (on top of regular private-dev devices:\n"); - printf("# "); + fprintf(fp, "\n"); + fprintf(fp, "# private-dev\n"); + fprintf(fp, "# This is the list of devices accessed (on top of regular private-dev devices:\n"); + fprintf(fp, "# "); FileDB *ptr = dev_out; while (ptr) { - printf("%s,", ptr->fname); + fprintf(fp, "%s,", ptr->fname); ptr = ptr->next; } - printf("\n"); + fprintf(fp, "\n"); } } diff --git a/src/fbuilder/build_home.c b/src/fbuilder/build_home.c index 947f172d8..9bbd2c258 100644 --- a/src/fbuilder/build_home.c +++ b/src/fbuilder/build_home.c @@ -158,7 +158,7 @@ void process_home(const char *fname, char *home, int home_len) { // process fname, fname.1, fname.2, fname.3, fname.4, fname.5 -void build_home(const char *fname) { +void build_home(const char *fname, FILE *fp) { assert(fname); // load whitelist common @@ -190,10 +190,10 @@ void build_home(const char *fname) { // print the out list if any if (db_out) { - filedb_print(db_out, "whitelist ~/"); - printf("include /etc/firejail/whitelist-common.inc\n"); + filedb_print(db_out, "whitelist ~/", fp); + fprintf(fp, "include /etc/firejail/whitelist-common.inc\n"); } else - printf("private\n"); + fprintf(fp, "private\n"); } \ No newline at end of file diff --git a/src/fbuilder/build_profile.c b/src/fbuilder/build_profile.c index 6d6263035..de9f79232 100644 --- a/src/fbuilder/build_profile.c +++ b/src/fbuilder/build_profile.c @@ -56,7 +56,7 @@ static void clear_tmp_files(void) { } -void build_profile(int argc, char **argv, int index) { +void build_profile(int argc, char **argv, int index, FILE *fp) { // next index is the application name if (index >= argc) { fprintf(stderr, "Error: application name missing\n"); @@ -116,51 +116,51 @@ void build_profile(int argc, char **argv, int index) { if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { printf("\n\n\n"); - printf("############################################\n"); - printf("# %s profile\n", argv[index]); - printf("############################################\n"); - printf("# Persistent global definitions\n"); - printf("# include /etc/firejail/globals.local\n"); - printf("\n"); + fprintf(fp, "############################################\n"); + fprintf(fp, "# %s profile\n", argv[index]); + fprintf(fp, "############################################\n"); + fprintf(fp, "# Persistent global definitions\n"); + fprintf(fp, "# include /etc/firejail/globals.local\n"); + fprintf(fp, "\n"); - printf("### basic blacklisting\n"); - printf("include /etc/firejail/disable-common.inc\n"); - printf("# include /etc/firejail/disable-devel.inc\n"); - printf("include /etc/firejail/disable-passwdmgr.inc\n"); - printf("# include /etc/firejail/disable-programs.inc\n"); - printf("\n"); + fprintf(fp, "### basic blacklisting\n"); + fprintf(fp, "include /etc/firejail/disable-common.inc\n"); + fprintf(fp, "# include /etc/firejail/disable-devel.inc\n"); + fprintf(fp, "include /etc/firejail/disable-passwdmgr.inc\n"); + fprintf(fp, "# include /etc/firejail/disable-programs.inc\n"); + fprintf(fp, "\n"); - printf("### home directory whitelisting\n"); - build_home(TRACE_OUTPUT); - printf("\n"); + fprintf(fp, "### home directory whitelisting\n"); + build_home(TRACE_OUTPUT, fp); + fprintf(fp, "\n"); - printf("### filesystem\n"); - build_tmp(TRACE_OUTPUT); - build_dev(TRACE_OUTPUT); - build_etc(TRACE_OUTPUT); - build_var(TRACE_OUTPUT); - build_bin(TRACE_OUTPUT); - build_share(TRACE_OUTPUT); - printf("\n"); + fprintf(fp, "### filesystem\n"); + build_tmp(TRACE_OUTPUT, fp); + build_dev(TRACE_OUTPUT, fp); + build_etc(TRACE_OUTPUT, fp); + build_var(TRACE_OUTPUT, fp); + build_bin(TRACE_OUTPUT, fp); + build_share(TRACE_OUTPUT, fp); + fprintf(fp, "\n"); - printf("### security filters\n"); - printf("caps.drop all\n"); - printf("nonewprivs\n"); - printf("seccomp\n"); + fprintf(fp, "### security filters\n"); + fprintf(fp, "caps.drop all\n"); + fprintf(fp, "nonewprivs\n"); + fprintf(fp, "seccomp\n"); if (have_strace) - build_seccomp(STRACE_OUTPUT); + build_seccomp(STRACE_OUTPUT, fp); else { - printf("# If you install strace on your system, Firejail will also create a\n"); - printf("# whitelisted seccomp filter.\n"); + fprintf(fp, "# If you install strace on your system, Firejail will also create a\n"); + fprintf(fp, "# whitelisted seccomp filter.\n"); } - printf("\n"); + fprintf(fp, "\n"); - printf("### network\n"); - build_protocol(TRACE_OUTPUT); - printf("\n"); + fprintf(fp, "### network\n"); + build_protocol(TRACE_OUTPUT, fp); + fprintf(fp, "\n"); - printf("### environment\n"); - printf("shell none\n"); + fprintf(fp, "### environment\n"); + fprintf(fp, "shell none\n"); } else { diff --git a/src/fbuilder/build_seccomp.c b/src/fbuilder/build_seccomp.c index 18a767518..63f37e34a 100644 --- a/src/fbuilder/build_seccomp.c +++ b/src/fbuilder/build_seccomp.c @@ -20,11 +20,12 @@ #include "fbuilder.h" -void build_seccomp(const char *fname) { +void build_seccomp(const char *fname, FILE *fp) { assert(fname); + assert(fp); - FILE *fp = fopen(fname, "r"); - if (!fp) { + FILE *fp2 = fopen(fname, "r"); + if (!fp2) { fprintf(stderr, "Error: cannot open %s\n", fname); exit(1); } @@ -33,7 +34,7 @@ void build_seccomp(const char *fname) { int line = 1; int position = 0; int cnt = 0; - while (fgets(buf, MAX_BUF, fp)) { + while (fgets(buf, MAX_BUF, fp2)) { // remove \n char *ptr = strchr(buf, '\n'); if (ptr) @@ -62,20 +63,20 @@ void build_seccomp(const char *fname) { break; if (line == 3) - printf("# seccomp.keep %s", buf + position); + fprintf(fp, "# seccomp.keep %s", buf + position); else - printf(",%s", buf + position); + fprintf(fp, ",%s", buf + position); cnt++; } line++; } - printf("\n"); - printf("# %d syscalls total\n", cnt); - printf("# Probably you will need to add more syscalls to seccomp.keep. Look for\n"); - printf("# seccomp errors in /var/log/syslog or /var/log/audit/audit.log while\n"); - printf("# running your sandbox.\n"); + fprintf(fp, "\n"); + fprintf(fp, "# %d syscalls total\n", cnt); + fprintf(fp, "# Probably you will need to add more syscalls to seccomp.keep. Look for\n"); + fprintf(fp, "# seccomp errors in /var/log/syslog or /var/log/audit/audit.log while\n"); + fprintf(fp, "# running your sandbox.\n"); - fclose(fp); + fclose(fp2); } //*************************************** @@ -141,7 +142,7 @@ static void process_protocol(const char *fname) { // process fname, fname.1, fname.2, fname.3, fname.4, fname.5 -void build_protocol(const char *fname) { +void build_protocol(const char *fname, FILE *fp) { assert(fname); // run fname @@ -161,31 +162,31 @@ void build_protocol(const char *fname) { int net = 0; if (unix_s || inet || inet6 || netlink || packet) { - printf("protocol "); + fprintf(fp, "protocol "); if (unix_s) - printf("unix,"); + fprintf(fp, "unix,"); if (inet) { - printf("inet,"); + fprintf(fp, "inet,"); net = 1; } if (inet6) { - printf("inet6,"); + fprintf(fp, "inet6,"); net = 1; } if (netlink) - printf("netlink,"); + fprintf(fp, "netlink,"); if (packet) { - printf("packet"); + fprintf(fp, "packet"); net = 1; } - printf("\n"); + fprintf(fp, "\n"); } if (net == 0) - printf("net none\n"); + fprintf(fp, "net none\n"); else { - printf("# net eth0\n"); - printf("netfilter\n"); + fprintf(fp, "# net eth0\n"); + fprintf(fp, "netfilter\n"); } } diff --git a/src/fbuilder/fbuilder.h b/src/fbuilder/fbuilder.h index 401ae908e..81dc951ec 100644 --- a/src/fbuilder/fbuilder.h +++ b/src/fbuilder/fbuilder.h @@ -32,24 +32,24 @@ extern int arg_debug; // build_profile.c -void build_profile(int argc, char **argv, int index); +void build_profile(int argc, char **argv, int index, FILE *fp); // build_seccomp.c -void build_seccomp(const char *fname); -void build_protocol(const char *fname); +void build_seccomp(const char *fname, FILE *fp); +void build_protocol(const char *fname, FILE *fp); // build_fs.c -void build_etc(const char *fname); -void build_var(const char *fname); -void build_tmp(const char *fname); -void build_dev(const char *fname); -void build_share(const char *fname); +void build_etc(const char *fname, FILE *fp); +void build_var(const char *fname, FILE *fp); +void build_tmp(const char *fname, FILE *fp); +void build_dev(const char *fname, FILE *fp); +void build_share(const char *fname, FILE *fp); // build_bin.c -void build_bin(const char *fname); +void build_bin(const char *fname, FILE *fp); // build_home.c -void build_home(const char *fname); +void build_home(const char *fname, FILE *fp); // utils.c int is_dir(const char *fname); @@ -64,6 +64,6 @@ typedef struct filedb_t { FileDB *filedb_add(FileDB *head, const char *fname); FileDB *filedb_find(FileDB *head, const char *fname); -void filedb_print(FileDB *head, const char *prefix); +void filedb_print(FileDB *head, const char *prefix, FILE *fp); #endif \ No newline at end of file diff --git a/src/fbuilder/filedb.c b/src/fbuilder/filedb.c index a76fbc961..b7162c2d6 100644 --- a/src/fbuilder/filedb.c +++ b/src/fbuilder/filedb.c @@ -69,10 +69,10 @@ FileDB *filedb_add(FileDB *head, const char *fname) { return entry; }; -void filedb_print(FileDB *head, const char *prefix) { +void filedb_print(FileDB *head, const char *prefix, FILE *fp) { FileDB *ptr = head; while (ptr) { - printf("%s%s\n", prefix, ptr->fname); + fprintf(fp, "%s%s\n", prefix, ptr->fname); ptr = ptr->next; } } diff --git a/src/fbuilder/main.c b/src/fbuilder/main.c index 83217ef98..1b997ccdb 100644 --- a/src/fbuilder/main.c +++ b/src/fbuilder/main.c @@ -22,7 +22,7 @@ int arg_debug = 0; static void usage(void) { printf("Firejail profile builder\n"); - printf("Usage: firejail [--debug] --build program-and-arguments\n"); + printf("Usage: firejail [--debug] --build[=profile-file] program-and-arguments\n"); } int main(int argc, char **argv) { @@ -38,6 +38,8 @@ printf("\n"); int i; int prog_index = 0; + FILE *fp = stdout; + int prof_file = 0; // parse arguments and extract program index for (i = 1; i < argc; i++) { @@ -49,6 +51,22 @@ printf("\n"); arg_debug = 1; else if (strcmp(argv[i], "--build") == 0) ; // do nothing, this is passed down from firejail + else if (strncmp(argv[i], "--build=", 8) == 0) { + // this option is only supported for non-root users + if (getuid() == 0) { + fprintf(stderr, "Error fbuild: --build=profile-name is not supported for root user.\n"); + exit(1); + } + + // check file access + fp = fopen(argv[i] + 8, "w"); + if (!fp) { + fprintf(stderr, "Error fbuild: cannot open profile file.\n"); + exit(1); + } + prof_file = 1; + // do nothing, this is passed down from firejail + } else { if (*argv[i] == '-') { fprintf(stderr, "Error fbuilder: invalid program\n"); @@ -63,9 +81,13 @@ printf("\n"); if (prog_index == 0) { fprintf(stderr, "Error fbuilder: program and arguments required\n"); usage(); + if (prof_file) + fclose(fp); exit(1); } - build_profile(argc, argv, prog_index); + build_profile(argc, argv, prog_index, fp); + if (prof_file) + fclose(fp); return 0; } diff --git a/src/firejail/main.c b/src/firejail/main.c index 126f98d9b..fef333601 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -831,13 +831,21 @@ char *guess_shell(void) { return shell; } -static int check_arg(int argc, char **argv, const char *argument) { +static int check_arg(int argc, char **argv, const char *argument, int strict) { int i; int found = 0; for (i = 1; i < argc; i++) { - if (strcmp(argv[i], argument) == 0) { - found = 1; - break; + if (strict) { + if (strcmp(argv[i], argument) == 0) { + found = 1; + break; + } + } + else { + if (strncmp(argv[i], argument, strlen(argument)) == 0) { + found = 1; + break; + } } // detect end of firejail params @@ -891,9 +899,9 @@ int main(int argc, char **argv) { preproc_build_firejail_dir(); preproc_clean_run(); - if (check_arg(argc, argv, "--quiet")) + if (check_arg(argc, argv, "--quiet", 1)) arg_quiet = 1; - if (check_arg(argc, argv, "--allow-debuggers")) { + if (check_arg(argc, argv, "--allow-debuggers", 1)) { // check kernel version struct utsname u; int rv = uname(&u); @@ -921,14 +929,14 @@ int main(int argc, char **argv) { #ifdef HAVE_GIT_INSTALL // process git-install and git-uninstall - if (check_arg(argc, argv, "--git-install")) + if (check_arg(argc, argv, "--git-install", 1)) git_install(); // this function will not return - if (check_arg(argc, argv, "--git-uninstall")) + if (check_arg(argc, argv, "--git-uninstall", 1)) git_uninstall(); // this function will not return #endif // profile builder - if (check_arg(argc, argv, "--build")) + if (check_arg(argc, argv, "--build", 0)) // supports both --build and --build=filename run_builder(argc, argv); // this function will not return // check argv[0] symlink wrapper if this is not a login shell @@ -946,10 +954,10 @@ int main(int argc, char **argv) { EUID_USER(); if (rv == 0) { // if --force option is passed to the program, disregard the existing sandbox - if (check_arg(argc, argv, "--force")) + if (check_arg(argc, argv, "--force", 1)) option_force = 1; else { - if (check_arg(argc, argv, "--version")) { + if (check_arg(argc, argv, "--version", 1)) { printf("firejail version %s\n", VERSION); exit(0); } @@ -966,7 +974,7 @@ int main(int argc, char **argv) { EUID_ROOT(); if (geteuid()) { // only --version is supported without SUID support - if (check_arg(argc, argv, "--version")) { + if (check_arg(argc, argv, "--version", 1)) { printf("firejail version %s\n", VERSION); exit(0); } diff --git a/src/firejail/usage.c b/src/firejail/usage.c index f3b3aace5..567d3134e 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c @@ -45,6 +45,7 @@ void usage(void) { #endif printf(" --blacklist=filename - blacklist directory or file.\n"); printf(" --build - build a whitelisted profile for the application.\n"); + printf(" --build=filename - build a whitelisted profile for the application.\n"); printf(" -c - execute command and exit.\n"); printf(" --caps - enable default Linux capabilities filter.\n"); printf(" --caps.drop=all - drop all capabilities.\n"); diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 00481d4d3..2303a8bbd 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -146,7 +146,7 @@ $ firejail "\-\-blacklist=/home/username/My Virtual Machines" $ firejail \-\-blacklist=/home/username/My\\ Virtual\\ Machines .TP \fB\-\-build -The command builds a whitelisted profile. If /usr/bin/strace is installed on the system, it also +The command builds a whitelisted profile. The profile is printed on the screen. If /usr/bin/strace is installed on the system, it also builds a whitelisted seccomp profile. The program is run in a very relaxed sandbox, with only --caps.drop=all and --nonewprivs. Programs that raise user privileges are not supported in order to allow strace to run. Chromium and Chromium-based browsers will not work. @@ -155,7 +155,19 @@ in order to allow strace to run. Chromium and Chromium-based browsers will not w .br Example: .br -$ firejail --build vlc ~/Videos/test.mp4 +$ firejail --build=profile-file vlc ~/Videos/test.mp4 +.TP +\fB\-\-build=profile-file +The command builds a whitelisted profile, and saves it in profile-file. If /usr/bin/strace is installed on the system, it also +builds a whitelisted seccomp profile. The program is run in a very relaxed sandbox, +with only --caps.drop=all and --nonewprivs. Programs that raise user privileges are not supported +in order to allow strace to run. Chromium and Chromium-based browsers will not work. +.br + +.br +Example: +.br +$ firejail --build=vlc.profile vlc ~/Videos/test.mp4 .TP \fB\-c Execute command and exit. -- cgit v1.2.3-70-g09d2