From ff1ba4d4b666386dece4728e62d78bff55eb983a Mon Sep 17 00:00:00 2001 From: "Kelvin M. Klann" Date: Sat, 1 Jul 2023 08:37:07 -0300 Subject: [PATCH 1/4] firecfg: fix missing free and formatting Changes: * fix inconsistent indentation/braces * add missing free --- src/firecfg/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/firecfg/main.c b/src/firecfg/main.c index 4ec81c5b3d9..90cea6d524d 100644 --- a/src/firecfg/main.c +++ b/src/firecfg/main.c @@ -165,12 +165,11 @@ static void set_file(const char *name, const char *firejail_exec) { if (rv) { fprintf(stderr, "Error: cannot create %s symbolic link\n", fname); perror("symlink"); - } - else + } else { printf(" %s created\n", name); - } - else { - fprintf(stderr, "Warning: cannot create %s - already exists! Skipping...\n", fname); + } + } else { + fprintf(stderr, "Warning: cannot create %s - already exists! Skipping...\n", fname); } free(fname); @@ -260,6 +259,7 @@ static void set_links_homedir(const char *homedir) { free(dirname); return; } + free(dirname); struct dirent *entry; while ((entry = readdir(dir))) { From 62162e3a49cd8db335733664d07ffd747a065928 Mon Sep 17 00:00:00 2001 From: "Kelvin M. Klann" Date: Fri, 30 Jun 2023 17:00:01 -0300 Subject: [PATCH 2/4] firecfg: turn constant strings into constants Instead of using asprintf + free. Also, use LIBDIR instead of hardcoded "/usr/lib" for fzenity. --- src/firecfg/firecfg.h | 6 +++++ src/firecfg/main.c | 62 +++++++++++++------------------------------ 2 files changed, 25 insertions(+), 43 deletions(-) diff --git a/src/firecfg/firecfg.h b/src/firecfg/firecfg.h index 8f74a119801..6e17c20cf3d 100644 --- a/src/firecfg/firecfg.h +++ b/src/firecfg/firecfg.h @@ -37,6 +37,12 @@ #include "../include/common.h" #define MAX_BUF 4096 +#define FIRECFG_CFGFILE SYSCONFDIR "/firecfg.config" +#define FIREJAIL_EXEC PREFIX "/bin/firejail" +#define FIREJAIL_WELCOME_SH LIBDIR "/firejail/firejail-welcome.sh" +#define FZENITY_EXEC LIBDIR "/firejail/fzenity" +#define ZENITY_EXEC "/usr/bin/zenity" +#define SUDO_EXEC "sudo" // main.c extern int arg_debug; diff --git a/src/firecfg/main.c b/src/firecfg/main.c index 90cea6d524d..0d995a6ddf3 100644 --- a/src/firecfg/main.c +++ b/src/firecfg/main.c @@ -76,10 +76,6 @@ static void list(void) { exit(1); } - char *firejail_exec; - if (asprintf(&firejail_exec, "%s/bin/firejail", PREFIX) == -1) - errExit("asprintf"); - struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) @@ -92,7 +88,7 @@ static void list(void) { if (is_link(fullname)) { char* fname = realpath(fullname, NULL); if (fname) { - if (strcmp(fname, firejail_exec) == 0) + if (strcmp(fname, FIREJAIL_EXEC) == 0) printf("%s\n", fullname); free(fname); } @@ -101,7 +97,6 @@ static void list(void) { } closedir(dir); - free(firejail_exec); } static void clean(void) { @@ -114,10 +109,6 @@ static void clean(void) { exit(1); } - char *firejail_exec; - if (asprintf(&firejail_exec, "%s/bin/firejail", PREFIX) == -1) - errExit("asprintf"); - struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) @@ -130,7 +121,7 @@ static void clean(void) { if (is_link(fullname)) { char* fname = realpath(fullname, NULL); if (fname) { - if (strcmp(fname, firejail_exec) == 0) { + if (strcmp(fname, FIREJAIL_EXEC) == 0) { char *ptr = strrchr(fullname, '/'); assert(ptr); ptr++; @@ -147,7 +138,6 @@ static void clean(void) { } closedir(dir); - free(firejail_exec); printf("\n"); } @@ -175,24 +165,16 @@ static void set_file(const char *name, const char *firejail_exec) { free(fname); } -// parse /etc/firejail/firecfg.config file -static void set_links_firecfg(void) { - char *cfgfile; - if (asprintf(&cfgfile, "%s/firecfg.config", SYSCONFDIR) == -1) - errExit("asprintf"); - - char *firejail_exec; - if (asprintf(&firejail_exec, "%s/bin/firejail", PREFIX) == -1) - errExit("asprintf"); +// parse a single config file +static void set_links_firecfg(const char *cfgfile) { + printf("Configuring symlinks in %s based on %s\n", arg_bindir, cfgfile); - // parse /etc/firejail/firecfg.config file FILE *fp = fopen(cfgfile, "r"); if (!fp) { perror("fopen"); fprintf(stderr, "Error: cannot open %s\n", cfgfile); exit(1); } - printf("Configuring symlinks in %s based on firecfg.config\n", arg_bindir); char buf[MAX_BUF]; int lineno = 0; @@ -223,12 +205,10 @@ static void set_links_firecfg(void) { continue; // set link - set_file(start, firejail_exec); + set_file(start, FIREJAIL_EXEC); } fclose(fp); - free(cfgfile); - free(firejail_exec); } // parse ~/.config/firejail/ directory @@ -245,10 +225,6 @@ static void set_links_homedir(const char *homedir) { return; } - char *firejail_exec; - if (asprintf(&firejail_exec, "%s/bin/firejail", PREFIX) == -1) - errExit("asprintf"); - // parse ~/.config/firejail/ directory printf("\nConfiguring symlinks in %s based on local firejail config directory\n", arg_bindir); @@ -280,12 +256,10 @@ static void set_links_homedir(const char *homedir) { } *ptr = '\0'; - set_file(exec, firejail_exec); + set_file(exec, FIREJAIL_EXEC); free(exec); } closedir(dir); - - free(firejail_exec); } static const char *get_sudo_user(void) { @@ -449,18 +423,20 @@ int main(int argc, char **argv) { } if (arg_guide) { + const char *zenity_exec; + if (arg_debug) + zenity_exec = FZENITY_EXEC; + else + zenity_exec = ZENITY_EXEC; + char *cmd; -if (arg_debug) { - if (asprintf(&cmd, "sudo %s/firejail/firejail-welcome.sh /usr/lib/firejail/fzenity %s %s", LIBDIR, SYSCONFDIR, user) == -1) + if (asprintf(&cmd, "%s %s %s %s %s", + SUDO_EXEC, FIREJAIL_WELCOME_SH, zenity_exec, SYSCONFDIR, user) == -1) errExit("asprintf"); -} -else { - if (asprintf(&cmd, "sudo %s/firejail/firejail-welcome.sh /usr/bin/zenity %s %s", LIBDIR, SYSCONFDIR, user) == -1) - errExit("asprintf"); -} + int status = system(cmd); if (status == -1) { - fprintf(stderr, "Error: cannot run firejail-welcome.sh\n"); + fprintf(stderr, "Error: cannot run %s\n", FIREJAIL_WELCOME_SH); exit(1); } free(cmd); @@ -474,8 +450,8 @@ else { // clear all symlinks clean(); - // set new symlinks based on /etc/firejail/firecfg.config - set_links_firecfg(); + // set new symlinks based on firecfg.config + set_links_firecfg(FIRECFG_CFGFILE); if (getuid() == 0) { // add user to firejail access database - only for root From 2993298aaa7b6e70dd1bfc1b698db77390f397fd Mon Sep 17 00:00:00 2001 From: "Kelvin M. Klann" Date: Thu, 29 Jun 2023 18:22:10 -0300 Subject: [PATCH 3/4] firecfg: parse config files in /etc/firejail/firecfg.d As suggested by @WhyNotHugo[1]. [1] https://github.com/netblue30/firejail/issues/2097#issuecomment-1179160459 --- Makefile | 1 + src/firecfg/firecfg.h | 4 ++++ src/firecfg/main.c | 30 +++++++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3055e226a16..c25e9f501d2 100644 --- a/Makefile +++ b/Makefile @@ -228,6 +228,7 @@ endif install -m 0644 -t $(DESTDIR)$(docdir) COPYING README RELNOTES etc/templates/* # profiles and settings install -m 0755 -d $(DESTDIR)$(sysconfdir)/firejail + install -m 0755 -d $(DESTDIR)$(sysconfdir)/firejail/firecfg.d install -m 0644 -t $(DESTDIR)$(sysconfdir)/firejail src/firecfg/firecfg.config install -m 0644 -t $(DESTDIR)$(sysconfdir)/firejail etc/profile-a-l/*.profile etc/profile-m-z/*.profile etc/inc/*.inc etc/net/*.net etc/firejail.config sh -c "if [ ! -f $(DESTDIR)/$(sysconfdir)/firejail/login.users ]; then install -c -m 0644 etc/login.users $(DESTDIR)/$(sysconfdir)/firejail/.; fi;" diff --git a/src/firecfg/firecfg.h b/src/firecfg/firecfg.h index 6e17c20cf3d..11e3ebc678a 100644 --- a/src/firecfg/firecfg.h +++ b/src/firecfg/firecfg.h @@ -37,7 +37,11 @@ #include "../include/common.h" #define MAX_BUF 4096 +// config files #define FIRECFG_CFGFILE SYSCONFDIR "/firecfg.config" +#define FIRECFG_CONF_GLOB SYSCONFDIR "/firecfg.d/*.conf" + +// programs #define FIREJAIL_EXEC PREFIX "/bin/firejail" #define FIREJAIL_WELCOME_SH LIBDIR "/firejail/firejail-welcome.sh" #define FZENITY_EXEC LIBDIR "/firejail/fzenity" diff --git a/src/firecfg/main.c b/src/firecfg/main.c index 0d995a6ddf3..35fa850f140 100644 --- a/src/firecfg/main.c +++ b/src/firecfg/main.c @@ -20,6 +20,8 @@ #include "firecfg.h" #include "../include/firejail_user.h" +#include + int arg_debug = 0; char *arg_bindir = "/usr/local/bin"; int arg_guide = 0; @@ -209,6 +211,29 @@ static void set_links_firecfg(const char *cfgfile) { } fclose(fp); + printf("\n"); +} + +// parse all config files matching pattern +static void set_links_firecfg_glob(const char *pattern) { + printf("Looking for config files in %s\n", pattern); + + glob_t globbuf; + int globerr = glob(pattern, 0, NULL, &globbuf); + if (globerr == GLOB_NOMATCH) { + fprintf(stderr, "No matches for glob pattern %s\n", pattern); + goto out; + } else if (globerr != 0) { + fprintf(stderr, "Warning: Failed to match glob pattern %s: %s\n", + pattern, strerror(errno)); + goto out; + } + + size_t i; + for (i = 0; i < globbuf.gl_pathc; i++) + set_links_firecfg(globbuf.gl_pathv[i]); +out: + globfree(&globbuf); } // parse ~/.config/firejail/ directory @@ -450,12 +475,15 @@ int main(int argc, char **argv) { // clear all symlinks clean(); + // set new symlinks based on .conf files + set_links_firecfg_glob(FIRECFG_CONF_GLOB); + // set new symlinks based on firecfg.config set_links_firecfg(FIRECFG_CFGFILE); if (getuid() == 0) { // add user to firejail access database - only for root - printf("\nAdding user %s to Firejail access database in %s/firejail.users\n", user, SYSCONFDIR); + printf("Adding user %s to Firejail access database in %s/firejail.users\n", user, SYSCONFDIR); // temporarily set the umask, access database must be world-readable mode_t orig_umask = umask(022); firejail_user_add(user); From ef6cfb8a22b6b788298a0601e837856b51c60e76 Mon Sep 17 00:00:00 2001 From: "Kelvin M. Klann" Date: Thu, 29 Jun 2023 19:54:21 -0300 Subject: [PATCH 4/4] firecfg: add ignore command and docs Add ignore command (`!PROGRAM`), as suggested by @WhyNotHugo[1]. It prevents firecfg from creating a symlink for the given program. Also, document the paths used and the config file syntax. Note that `/etc/firejail/firecfg.d/*.conf` files are parsed before /etc/firejail/firecfg.config, so the former can ignore/override any item in the latter. Closes #2097. [1] https://github.com/netblue30/firejail/issues/2097#issuecomment-1179160459 --- src/firecfg/main.c | 45 +++++++++++++++++++++++++++++++++- src/man/firecfg.1.in | 57 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/src/firecfg/main.c b/src/firecfg/main.c index 35fa850f140..604b126333e 100644 --- a/src/firecfg/main.c +++ b/src/firecfg/main.c @@ -143,6 +143,40 @@ static void clean(void) { printf("\n"); } +#define ignorelist_maxlen 2048 +static const char *ignorelist[ignorelist_maxlen]; +static int ignorelist_len = 0; + +static int append_ignorelist(const char *const str) { + assert(str); + if (ignorelist_len >= ignorelist_maxlen) { + fprintf(stderr, "Warning: Ignore list is full (%d/%d), skipping %s\n", + ignorelist_len, ignorelist_maxlen, str); + return 0; + } + + printf(" ignoring '%s'\n", str); + const char *const dup = strdup(str); + if (!dup) + errExit("strdup"); + + ignorelist[ignorelist_len] = dup; + ignorelist_len++; + + return 1; +} + +static int in_ignorelist(const char *const str) { + assert(str); + int i; + for (i = 0; i < ignorelist_len; i++) { + if (strcmp(str, ignorelist[i]) == 0) + return 1; + } + + return 0; +} + static void set_file(const char *name, const char *firejail_exec) { if (which(name) == 0) return; @@ -206,8 +240,17 @@ static void set_links_firecfg(const char *cfgfile) { if (*start == '\0') continue; + // handle ignore command + if (*start == '!') { + append_ignorelist(start + 1); + continue; + } + // set link - set_file(start, FIREJAIL_EXEC); + if (!in_ignorelist(start)) + set_file(start, FIREJAIL_EXEC); + else + printf(" %s ignored\n", start); } fclose(fp); diff --git a/src/man/firecfg.1.in b/src/man/firecfg.1.in index a85fbc5dadb..e43a573de5e 100644 --- a/src/man/firecfg.1.in +++ b/src/man/firecfg.1.in @@ -29,9 +29,13 @@ Note: The examples use \fBsudo\fR, but \fBdoas\fR is also supported. To set it up, run "sudo firecfg" after installing Firejail software. The same command should also be run after installing new programs. If the program is supported by Firejail, the symbolic link in /usr/local/bin -will be created. For a full list of programs supported by default run "cat /etc/firejail/firecfg.config". - -For user-driven manual integration, see \fBDESKTOP INTEGRATION\fR section in \fBman 1 firejail\fR. +will be created. +.PP +To configure the list of programs used by firecfg when creating symlinks, see +\fBFILES\fR and \fBSYNTAX\fR. +.PP +For user-driven manual integration, see \fBDESKTOP INTEGRATION\fR section in +\fBman 1 firejail\fR. .SH DEFAULT ACTIONS The following actions are implemented by default by running sudo firecfg: @@ -135,8 +139,53 @@ $ sudo firecfg --clean /usr/local/bin/vlc removed .br [...] +.SH FILES +.PP +Configuration files are searched for and parsed in the following paths: +.PP +.RS +1. /etc/firejail/firecfg.d/*.conf (in alphabetical order) +.br +2. /etc/firejail/firecfg.config +.RE +.PP +The programs that are supported by default are listed in +/etc/firejail/firecfg.config. +It is recommended to leave it as is and put all customizations inside +/etc/firejail/firecfg.d/. +.PP +Profile files are also searched in the user configuration directory: +.PP +.RS +3. ~/.config/firejail/*.profile +.RE +.PP +For every \fBPROGRAM.profile\fR file found, firecfg attempts to create a +symlink for "PROGRAM", as if "PROGRAM" was listed in a configuration file. +.SH SYNTAX +Configuration file syntax: +.PP +A line that starts with \fB#\fR is considered a comment. +.br +A line that starts with \fB!PROGRAM\fR means to ignore "PROGRAM" when creating +symlinks. +.br +A line that starts with anything else is considered to be the name of an +executable and firecfg will attempt to create a symlink for it. +.PP +For example, to prevent firecfg from creating symlinks for "firefox" and +"patch" while attempting to create a symlink for "myprog", the following lines +could be added to /etc/firejail/firecfg.d/10-my.conf: +.PP +.RS +!firefox +.br +!patch +.br - +.br +myprog +.RE .SH LICENSE This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. .PP