From 280f37eba89ebc211d0c02848d3d47d086458b25 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Sat, 16 Sep 2017 08:49:05 -0400 Subject: --build --- src/fbuilder/Makefile.in | 45 +++++++ src/fbuilder/build_fs.c | 276 +++++++++++++++++++++++++++++++++++++++++++ src/fbuilder/build_home.c | 199 +++++++++++++++++++++++++++++++ src/fbuilder/build_profile.c | 165 ++++++++++++++++++++++++++ src/fbuilder/build_seccomp.c | 191 ++++++++++++++++++++++++++++++ src/fbuilder/fbuilder.h | 65 ++++++++++ src/fbuilder/filedb.c | 79 +++++++++++++ src/fbuilder/main.c | 71 +++++++++++ src/fbuilder/utils.c | 72 +++++++++++ src/firejail/main.c | 22 ++++ 10 files changed, 1185 insertions(+) create mode 100644 src/fbuilder/Makefile.in create mode 100644 src/fbuilder/build_fs.c create mode 100644 src/fbuilder/build_home.c create mode 100644 src/fbuilder/build_profile.c create mode 100644 src/fbuilder/build_seccomp.c create mode 100644 src/fbuilder/fbuilder.h create mode 100644 src/fbuilder/filedb.c create mode 100644 src/fbuilder/main.c create mode 100644 src/fbuilder/utils.c (limited to 'src') diff --git a/src/fbuilder/Makefile.in b/src/fbuilder/Makefile.in new file mode 100644 index 000000000..dd8e2ce6e --- /dev/null +++ b/src/fbuilder/Makefile.in @@ -0,0 +1,45 @@ +all: fbuilder + +CC=@CC@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +sysconfdir=@sysconfdir@ + +VERSION=@PACKAGE_VERSION@ +NAME=@PACKAGE_NAME@ +HAVE_SECCOMP_H=@HAVE_SECCOMP_H@ +HAVE_SECCOMP=@HAVE_SECCOMP@ +HAVE_CHROOT=@HAVE_CHROOT@ +HAVE_BIND=@HAVE_BIND@ +HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ +HAVE_NETWORK=@HAVE_NETWORK@ +HAVE_USERNS=@HAVE_USERNS@ +HAVE_X11=@HAVE_X11@ +HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ +HAVE_WHITELIST=@HAVE_WHITELIST@ +HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ +HAVE_APPARMOR=@HAVE_APPARMOR@ +HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ +HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@ +EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ +HAVE_GCOV=@HAVE_GCOV@ +EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ + +H_FILE_LIST = $(sort $(wildcard *.[h])) +C_FILE_LIST = $(sort $(wildcard *.c)) +OBJS = $(C_FILE_LIST:.c=.o) +BINOBJS = $(foreach file, $(OBJS), $file) +CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security +LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread + +%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +fbuilder: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) + +clean:; rm -f *.o fbuilder *.gcov *.gcda *.gcno + +distclean: clean + rm -fr Makefile diff --git a/src/fbuilder/build_fs.c b/src/fbuilder/build_fs.c new file mode 100644 index 000000000..76281a54d --- /dev/null +++ b/src/fbuilder/build_fs.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "fbuilder.h" + +// common file processing function, using the callback for each line in the file +static void process_file(const char *fname, const char *dir, void (*callback)(char *)) { + assert(fname); + assert(dir); + assert(callback); + + int dir_len = strlen(dir); + + // process trace file + FILE *fp = fopen(fname, "r"); + if (!fp) { + fprintf(stderr, "Error: cannot open %s\n", fname); + exit(1); + } + + char buf[MAX_BUF]; + while (fgets(buf, MAX_BUF, fp)) { + // remove \n + char *ptr = strchr(buf, '\n'); + if (ptr) + *ptr = '\0'; + + // parse line: 4:galculator:access /etc/fonts/conf.d:0 + // number followed by : + ptr = buf; + if (!isdigit(*ptr)) + continue; + while (isdigit(*ptr)) + ptr++; + if (*ptr != ':') + continue; + ptr++; + + // next : + ptr = strchr(ptr, ':'); + if (!ptr) + continue; + ptr++; + if (strncmp(ptr, "access ", 7) == 0) + ptr += 7; + else if (strncmp(ptr, "fopen ", 6) == 0) + ptr += 6; + else if (strncmp(ptr, "fopen64 ", 8) == 0) + ptr += 8; + else if (strncmp(ptr, "open64 ", 7) == 0) + ptr += 7; + else if (strncmp(ptr, "open ", 5) == 0) + ptr += 5; + else + continue; + if (strncmp(ptr, dir, dir_len) != 0) + continue; + + // end of filename + char *ptr2 = strchr(ptr, ':'); + if (!ptr2) + continue; + *ptr2 = '\0'; + + callback(ptr); + } + + fclose(fp); +} + +// process fname, fname.1, fname.2, fname.3, fname.4, fname.5 +static void process_files(const char *fname, const char *dir, void (*callback)(char *)) { + assert(fname); + assert(dir); + assert(callback); + + // run fname + process_file(fname, dir, callback); + + // run all the rest + struct stat s; + int i; + for (i = 1; i <= 5; i++) { + char *newname; + if (asprintf(&newname, "%s.%d", fname, i) == -1) + errExit("asprintf"); + if (stat(newname, &s) == 0) + process_file(newname, dir, callback); + free(newname); + } +} + +//******************************************* +// etc directory +//******************************************* +static FileDB *etc_out = NULL; + +static void etc_callback(char *ptr) { + // skip firejail directory + if (strncmp(ptr, "/etc/firejail", 13) == 0) + return; + + // add only top files and directories + ptr += 5; // skip "/etc/" + char *end = strchr(ptr, '/'); + if (end) + *end = '\0'; + etc_out = filedb_add(etc_out, ptr); +} + +void build_etc(const char *fname) { + assert(fname); + + process_files(fname, "/etc", etc_callback); + + printf("private-etc "); + if (etc_out == NULL) + printf("none\n"); + else { + FileDB *ptr = etc_out; + while (ptr) { + printf("%s,", ptr->fname); + ptr = ptr->next; + } + printf("\n"); + } +} + +//******************************************* +// var directory +//******************************************* +static FileDB *var_out = NULL; +static void var_callback(char *ptr) { + if (strncmp(ptr, "/var/lib/menu-xdg", 17) == 0) + var_out = filedb_add(var_out, "/var/lib/menu-xdg"); + else if (strncmp(ptr, "/var/cache/fontconfig", 21) == 0) + var_out = filedb_add(var_out, "/var/cache/fontconfig"); + else + var_out = filedb_add(var_out, ptr); +} + +void build_var(const char *fname) { + assert(fname); + + process_files(fname, "/var", var_callback); + + if (var_out == NULL) + printf("blacklist /var\n"); + else + filedb_print(var_out, "whitelist "); +} + +//******************************************* +// tmp directory +//******************************************* +static FileDB *tmp_out = NULL; +static void tmp_callback(char *ptr) { + filedb_add(tmp_out, ptr); +} + +void build_tmp(const char *fname) { + assert(fname); + + process_files(fname, "/tmp", tmp_callback); + + if (tmp_out == NULL) + printf("private-tmp\n"); + else { + printf("\n"); + printf("# private-tmp\n"); + printf("# File accessed in /tmp directory:\n"); + printf("# "); + FileDB *ptr = tmp_out; + while (ptr) { + printf("%s,", ptr->fname); + ptr = ptr->next; + } + printf("\n"); + } +} + +//******************************************* +// dev directory +//******************************************* +static char *dev_skip[] = { + "/dev/zero", + "/dev/null", + "/dev/full", + "/dev/random", + "/dev/urandom", + "/dev/tty", + "/dev/snd", + "/dev/dri", + "/dev/pts", + "/dev/nvidia0", + "/dev/nvidia1", + "/dev/nvidia2", + "/dev/nvidia3", + "/dev/nvidia4", + "/dev/nvidia5", + "/dev/nvidia6", + "/dev/nvidia7", + "/dev/nvidia8", + "/dev/nvidia9", + "/dev/nvidiactl", + "/dev/nvidia-modeset", + "/dev/nvidia-uvm", + "/dev/video0", + "/dev/video1", + "/dev/video2", + "/dev/video3", + "/dev/video4", + "/dev/video5", + "/dev/video6", + "/dev/video7", + "/dev/video8", + "/dev/video9", + "/dev/dvb", + "/dev/sr0", + NULL +}; + +static FileDB *dev_out = NULL; +static void dev_callback(char *ptr) { + // skip private-dev devices + int i = 0; + int found = 0; + while (dev_skip[i]) { + if (strcmp(ptr, dev_skip[i]) == 0) { + found = 1; + break; + } + i++; + } + if (!found) + filedb_add(dev_out, ptr); +} + +void build_dev(const char *fname) { + assert(fname); + + process_files(fname, "/tmp", tmp_callback); + + if (dev_out == NULL) + printf("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("# "); + FileDB *ptr = dev_out; + while (ptr) { + printf("%s,", ptr->fname); + ptr = ptr->next; + } + printf("\n"); + } +} + diff --git a/src/fbuilder/build_home.c b/src/fbuilder/build_home.c new file mode 100644 index 000000000..947f172d8 --- /dev/null +++ b/src/fbuilder/build_home.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "fbuilder.h" + +static FileDB *db_skip = NULL; +static FileDB *db_out = NULL; + +static void load_whitelist_common(void) { + FILE *fp = fopen("/etc/firejail/whitelist-common.inc", "r"); + if (!fp) { + fprintf(stderr, "Error: cannot open whitelist-common.inc\n"); + exit(1); + } + + char buf[MAX_BUF]; + while (fgets(buf, MAX_BUF, fp)) { + if (strncmp(buf, "whitelist ~/", 12) != 0) + continue; + char *fn = buf + 12; + char *ptr = strchr(buf, '\n'); + if (!ptr) + continue; + *ptr = '\0'; + + // add the file to skip list + db_skip = filedb_add(db_skip, fn); + } + + fclose(fp); +} + +void process_home(const char *fname, char *home, int home_len) { + assert(fname); + assert(home); + assert(home_len); + + // process trace file + FILE *fp = fopen(fname, "r"); + if (!fp) { + fprintf(stderr, "Error: cannot open %s\n", fname); + exit(1); + } + + char buf[MAX_BUF]; + while (fgets(buf, MAX_BUF, fp)) { + // remove \n + char *ptr = strchr(buf, '\n'); + if (ptr) + *ptr = '\0'; + + // parse line: 4:galculator:access /etc/fonts/conf.d:0 + // number followed by : + ptr = buf; + if (!isdigit(*ptr)) + continue; + while (isdigit(*ptr)) + ptr++; + if (*ptr != ':') + continue; + ptr++; + + // next : + ptr = strchr(ptr, ':'); + if (!ptr) + continue; + ptr++; + if (strncmp(ptr, "access /home", 12) == 0) + ptr += 7; + else if (strncmp(ptr, "fopen /home", 11) == 0) + ptr += 6; + else if (strncmp(ptr, "fopen64 /home", 13) == 0) + ptr += 8; + else if (strncmp(ptr, "open64 /home", 12) == 0) + ptr += 7; + else if (strncmp(ptr, "open /home", 10) == 0) + ptr += 5; + else + continue; + + // end of filename + char *ptr2 = strchr(ptr, ':'); + if (!ptr2) + continue; + *ptr2 = '\0'; + + // check home directory + if (strncmp(ptr, home, home_len) != 0) + continue; + if (strcmp(ptr, home) == 0) + continue; + ptr += home_len + 1; + + // skip files handled automatically by firejail + if (strcmp(ptr, ".Xauthority") == 0 || + strcmp(ptr, ".Xdefaults-debian") == 0 || + strncmp(ptr, ".config/pulse/", 13) == 0 || + strncmp(ptr, ".pulse/", 7) == 0 || + strncmp(ptr, ".bash_hist", 10) == 0 || + strcmp(ptr, ".bashrc") == 0) + continue; + + + // try to find the relevant directory for this file + char *dir = extract_dir(ptr); + char *toadd = (dir)? dir: ptr; + + // skip some dot directories + if (strcmp(toadd, ".config") == 0 || + strcmp(toadd, ".local") == 0 || + strcmp(toadd, ".local/share") == 0 || + strcmp(toadd, ".cache") == 0) { + if (dir) + free(dir); + continue; + } + + // clean .cache entries + if (strncmp(toadd, ".cache/", 7) == 0) { + char *ptr2 = toadd + 7; + ptr2 = strchr(ptr2, '/'); + if (ptr2) + *ptr2 = '\0'; + } + + // skip files and directories in whitelist-common.inc + if (filedb_find(db_skip, toadd)) { + if (dir) + free(dir); + continue; + } + + // add the file to out list + db_out = filedb_add(db_out, toadd); + if (dir) + free(dir); + + } + fclose(fp); +} + + +// process fname, fname.1, fname.2, fname.3, fname.4, fname.5 +void build_home(const char *fname) { + assert(fname); + + // load whitelist common + load_whitelist_common(); + + // find user home directory + struct passwd *pw = getpwuid(getuid()); + if (!pw) + errExit("getpwuid"); + char *home = pw->pw_dir; + if (!home) + errExit("getpwuid"); + int home_len = strlen(home); + + // run fname + process_home(fname, home, home_len); + + // run all the rest + struct stat s; + int i; + for (i = 1; i <= 5; i++) { + char *newname; + if (asprintf(&newname, "%s.%d", fname, i) == -1) + errExit("asprintf"); + if (stat(newname, &s) == 0) + process_home(newname, home, home_len); + free(newname); + } + + // print the out list if any + if (db_out) { + filedb_print(db_out, "whitelist ~/"); + printf("include /etc/firejail/whitelist-common.inc\n"); + } + else + printf("private\n"); + +} \ No newline at end of file diff --git a/src/fbuilder/build_profile.c b/src/fbuilder/build_profile.c new file mode 100644 index 000000000..5fca22648 --- /dev/null +++ b/src/fbuilder/build_profile.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "fbuilder.h" +#include +#include + +#define TRACE_OUTPUT "/tmp/firejail-trace" +#define STRACE_OUTPUT "/tmp/firejail-strace" + +static char *cmdlist[] = { + "/usr/bin/firejail", + "--quiet", + "--output=" TRACE_OUTPUT, + "--noprofile", + "--caps.drop=all", + "--nonewprivs", + "--trace", + "/usr/bin/strace", // also used as a marker in build_profile() + "-c", + "-f", + "-o" STRACE_OUTPUT, +}; + +static void clear_tmp_files(void) { + unlink(STRACE_OUTPUT); + unlink(TRACE_OUTPUT); + + // run all the rest + int i; + for (i = 1; i <= 5; i++) { + char *newname; + if (asprintf(&newname, "%s.%d", TRACE_OUTPUT, i) == -1) + errExit("asprintf"); + unlink(newname); + free(newname); + } + +} + +void build_profile(int argc, char **argv, int index) { + unlink("/tmp/strace-output"); + + // next index is the application name + if (index >= argc) { + fprintf(stderr, "Error: application name missing\n"); + exit(1); + } + + // clean /tmp files + clear_tmp_files(); + + // detect strace + int have_strace = 0; + if (access("/usr/bin/strace", X_OK) == 0) + have_strace = 1; + + // calculate command length + int len = (int) sizeof(cmdlist) / sizeof(char*) + argc - index + 1; + if (arg_debug) + printf("command len %d + %d + 1\n", (int) (sizeof(cmdlist) / sizeof(char*)), argc - index); + char *cmd[len]; + + // build command + int i = 0; + for (i = 0; i < (int) sizeof(cmdlist) / sizeof(char*); i++) { + // skip strace if not installed + if (have_strace == 0 && strcmp(cmdlist[i], "/usr/bin/strace") == 0) + break; + cmd[i] = cmdlist[i]; + } + + int i2 = index; + for (; i < (len - 1); i++, i2++) + cmd[i] = argv[i2]; + cmd[i] = NULL; + + if (arg_debug) { + for (i = 0; i < len; i++) + printf("\t%s\n", cmd[i]); + } + + // fork and execute + pid_t child = fork(); + if (child == -1) + errExit("fork"); + if (child == 0) { + int rv = execvp(cmd[0], cmd); + errExit("execv"); + } + + // wait for all processes to finish + int status; + if (waitpid(child, &status, 0) != child) + errExit("waitpid"); + + 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"); + + 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"); + + printf("### home directory whitelisting\n"); + build_home(TRACE_OUTPUT); + printf("\n"); + + printf("### filesystem\n"); + build_tmp(TRACE_OUTPUT); + build_dev(TRACE_OUTPUT); + build_etc(TRACE_OUTPUT); + build_var(TRACE_OUTPUT); + printf("\n"); + + printf("### security filters\n"); + printf("caps.drop all\n"); + printf("nonewprivs\n"); + printf("seccomp\n"); + if (have_strace) + build_seccomp(STRACE_OUTPUT); + else { + printf("# If you install strace on your system, Firejail will also create a\n"); + printf("# whitelisted seccomp filter.\n"); + } + printf("\n"); + + printf("### network\n"); + build_protocol(TRACE_OUTPUT); + printf("\n"); + + printf("### environment\n"); + printf("shell none\n"); + + } + else { + fprintf(stderr, "Error: cannot run the sandbox\n"); + exit(1); + } +} diff --git a/src/fbuilder/build_seccomp.c b/src/fbuilder/build_seccomp.c new file mode 100644 index 000000000..18a767518 --- /dev/null +++ b/src/fbuilder/build_seccomp.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "fbuilder.h" + +void build_seccomp(const char *fname) { + assert(fname); + + FILE *fp = fopen(fname, "r"); + if (!fp) { + fprintf(stderr, "Error: cannot open %s\n", fname); + exit(1); + } + + char buf[MAX_BUF]; + int line = 1; + int position = 0; + int cnt = 0; + while (fgets(buf, MAX_BUF, fp)) { + // remove \n + char *ptr = strchr(buf, '\n'); + if (ptr) + *ptr = '\0'; + + // first line: + //% time seconds usecs/call calls errors syscall + if (line == 1) { + // extract syscall position + ptr = strstr(buf, "syscall"); + if (*buf != '%' || ptr == NULL) { + // skip this line, it could be garbage from strace + continue; + } + position = (int) (ptr - buf); + } + else if (line == 2) { + if (*buf != '-') { + fprintf(stderr, "Error: invalid strace output\n%s\n", buf); + exit(1); + } + } + else { + // get out on the next "----" line + if (*buf == '-') + break; + + if (line == 3) + printf("# seccomp.keep %s", buf + position); + else + printf(",%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"); + + fclose(fp); +} + +//*************************************** +// protocol +//*************************************** +int unix_s = 0; +int inet = 0; +int inet6 = 0; +int netlink = 0; +int packet = 0; +static void process_protocol(const char *fname) { + assert(fname); + + // process trace file + FILE *fp = fopen(fname, "r"); + if (!fp) { + fprintf(stderr, "Error: cannot open %s\n", fname); + exit(1); + } + + char buf[MAX_BUF]; + while (fgets(buf, MAX_BUF, fp)) { + // remove \n + char *ptr = strchr(buf, '\n'); + if (ptr) + *ptr = '\0'; + + // parse line: 4:galculator:access /etc/fonts/conf.d:0 + // number followed by : + ptr = buf; + if (!isdigit(*ptr)) + continue; + while (isdigit(*ptr)) + ptr++; + if (*ptr != ':') + continue; + ptr++; + + // next : + ptr = strchr(ptr, ':'); + if (!ptr) + continue; + ptr++; + if (strncmp(ptr, "socket ", 7) == 0) + ptr += 7; + else + continue; + + if (strncmp(ptr, "AF_LOCAL ", 9) == 0) + unix_s = 1; + else if (strncmp(ptr, "AF_INET ", 8) == 0) + inet = 1; + else if (strncmp(ptr, "AF_INET6 ", 9) == 0) + inet6 = 1; + else if (strncmp(ptr, "AF_NETLINK ", 9) == 0) + netlink = 1; + else if (strncmp(ptr, "AF_PACKET ", 9) == 0) + packet = 1; + } + + fclose(fp); +} + + +// process fname, fname.1, fname.2, fname.3, fname.4, fname.5 +void build_protocol(const char *fname) { + assert(fname); + + // run fname + process_protocol(fname); + + // run all the rest + struct stat s; + int i; + for (i = 1; i <= 5; i++) { + char *newname; + if (asprintf(&newname, "%s.%d", fname, i) == -1) + errExit("asprintf"); + if (stat(newname, &s) == 0) + process_protocol(newname); + free(newname); + } + + int net = 0; + if (unix_s || inet || inet6 || netlink || packet) { + printf("protocol "); + if (unix_s) + printf("unix,"); + if (inet) { + printf("inet,"); + net = 1; + } + if (inet6) { + printf("inet6,"); + net = 1; + } + if (netlink) + printf("netlink,"); + if (packet) { + printf("packet"); + net = 1; + } + printf("\n"); + } + + if (net == 0) + printf("net none\n"); + else { + printf("# net eth0\n"); + printf("netfilter\n"); + } +} + diff --git a/src/fbuilder/fbuilder.h b/src/fbuilder/fbuilder.h new file mode 100644 index 000000000..a9049ea2d --- /dev/null +++ b/src/fbuilder/fbuilder.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef FBUILDER_H +#define FBUILDER_H +#include "../include/common.h" +#include +#include +#include +#include + + +#define MAX_BUF 4096 +// main.c +extern int arg_debug; + +// build_profile.c +void build_profile(int argc, char **argv, int index); + +// build_seccomp.c +void build_seccomp(const char *fname); +void build_protocol(const char *fname); + +// 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); + +// build_home.c +void build_home(const char *fname); + +// utils.c +int is_dir(const char *fname); +char *extract_dir(char *fname); + +// filedb.c +typedef struct filedb_t { + struct filedb_t *next; + char *fname; // file name + int len; // length of file name +} FileDB; + +FileDB *filedb_add(FileDB *head, const char *fname); +FileDB *filedb_find(FileDB *head, const char *fname); +void filedb_print(FileDB *head, const char *prefix); + +#endif \ No newline at end of file diff --git a/src/fbuilder/filedb.c b/src/fbuilder/filedb.c new file mode 100644 index 000000000..a76fbc961 --- /dev/null +++ b/src/fbuilder/filedb.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "fbuilder.h" + +FileDB *filedb_find(FileDB *head, const char *fname) { + FileDB *ptr = head; + int found = 0; + int len = strlen(fname); + + while (ptr) { + // exact name + if (strcmp(fname, ptr->fname) == 0) { + found = 1; + break; + } + + // parent directory in the list + if (len > ptr->len && + fname[ptr->len] == '/' && + strncmp(ptr->fname, fname, ptr->len) == 0) { + found = 1; + break; + } + + ptr = ptr->next; + } + + if (found) + return ptr; + + return NULL; +} + +FileDB *filedb_add(FileDB *head, const char *fname) { + assert(fname); + + // don't add it if it is already there or if the parent directory is already in the list + if (filedb_find(head, fname)) + return head; + + // add a new entry + FileDB *entry = malloc(sizeof(FileDB)); + if (!entry) + errExit("malloc"); + memset(entry, 0, sizeof(FileDB)); + entry->fname = strdup(fname); + if (!entry->fname) + errExit("strdup"); + entry->len = strlen(entry->fname); + entry->next = head; + return entry; +}; + +void filedb_print(FileDB *head, const char *prefix) { + FileDB *ptr = head; + while (ptr) { + printf("%s%s\n", prefix, ptr->fname); + ptr = ptr->next; + } +} + diff --git a/src/fbuilder/main.c b/src/fbuilder/main.c new file mode 100644 index 000000000..83217ef98 --- /dev/null +++ b/src/fbuilder/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "fbuilder.h" +int arg_debug = 0; + +static void usage(void) { + printf("Firejail profile builder\n"); + printf("Usage: firejail [--debug] --build program-and-arguments\n"); +} + +int main(int argc, char **argv) { +#if 0 +{ +system("cat /proc/self/status"); +int i; +for (i = 0; i < argc; i++) + printf("*%s* ", argv[i]); +printf("\n"); +} +#endif + + int i; + int prog_index = 0; + + // parse arguments and extract program index + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") ==0) { + usage(); + return 0; + } + else if (strcmp(argv[i], "--debug") == 0) + arg_debug = 1; + else if (strcmp(argv[i], "--build") == 0) + ; // do nothing, this is passed down from firejail + else { + if (*argv[i] == '-') { + fprintf(stderr, "Error fbuilder: invalid program\n"); + usage(); + exit(1); + } + prog_index = i; + break; + } + } + + if (prog_index == 0) { + fprintf(stderr, "Error fbuilder: program and arguments required\n"); + usage(); + exit(1); + } + + build_profile(argc, argv, prog_index); + return 0; +} diff --git a/src/fbuilder/utils.c b/src/fbuilder/utils.c new file mode 100644 index 000000000..902290899 --- /dev/null +++ b/src/fbuilder/utils.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "fbuilder.h" + +// todo: duplicated from src/firejail/util.c - remove dplication +// return 1 if the file is a directory +int is_dir(const char *fname) { + assert(fname); + if (*fname == '\0') + return 0; + + // if fname doesn't end in '/', add one + int rv; + struct stat s; + if (fname[strlen(fname) - 1] == '/') + rv = stat(fname, &s); + else { + char *tmp; + if (asprintf(&tmp, "%s/", fname) == -1) { + fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__); + errExit("asprintf"); + } + rv = stat(tmp, &s); + free(tmp); + } + + if (rv == -1) + return 0; + + if (S_ISDIR(s.st_mode)) + return 1; + + return 0; +} + +// return NULL if fname is already a directory, or if no directory found +char *extract_dir(char *fname) { + assert(fname); + if (is_dir(fname)) + return NULL; + + char *name = strdup(fname); + if (!name) + errExit("strdup"); + + char *ptr = strrchr(name, '/'); + if (!ptr) { + free(name); + return NULL; + } + *ptr = '\0'; + + return name; +} diff --git a/src/firejail/main.c b/src/firejail/main.c index 399770142..1b49c5fb3 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -849,6 +849,24 @@ static int check_arg(int argc, char **argv, const char *argument) { return found; } +static void run_builder(int argc, char **argv) { + EUID_ASSERT(); + + // drop privileges + if (setgid(getgid()) < 0) + errExit("setgid/getgid"); + if (setuid(getuid()) < 0) + errExit("setuid/getuid"); + assert(getenv("LD_PRELOAD") == NULL); + + argv[0] = LIBDIR "/firejail/fbuilder"; + execvp(argv[0], argv); + + perror("execvp"); + exit(1); +} + + //******************************************* // Main program //******************************************* @@ -907,6 +925,10 @@ int main(int argc, char **argv) { git_uninstall(); // this function will not return #endif + // profile builder + if (check_arg(argc, argv, "--build")) + run_builder(argc, argv); // this function will not return + // check argv[0] symlink wrapper if this is not a login shell if (*argv[0] != '-') run_symlink(argc, argv); // this function will not return -- cgit v1.2.3-54-g00ecf