aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common.mk.in2
-rw-r--r--src/fids/Makefile.in18
-rw-r--r--src/fids/blake2b.c176
-rw-r--r--src/fids/config16
-rw-r--r--src/fids/db.c158
-rw-r--r--src/fids/db_exclude.c56
-rw-r--r--src/fids/fids.h51
-rw-r--r--src/fids/main.c371
-rw-r--r--src/firecfg/firecfg.config1
-rw-r--r--src/firejail/firejail.h9
-rw-r--r--src/firejail/ids.c89
-rw-r--r--src/firejail/main.c4
-rw-r--r--src/firejail/usage.c2
-rwxr-xr-xsrc/tools/profcleaner.sh8
14 files changed, 952 insertions, 9 deletions
diff --git a/src/common.mk.in b/src/common.mk.in
index 5ae8bf204..d117433dc 100644
--- a/src/common.mk.in
+++ b/src/common.mk.in
@@ -40,7 +40,7 @@ BINOBJS = $(foreach file, $(OBJS), $file)
40 40
41CFLAGS = @CFLAGS@ 41CFLAGS = @CFLAGS@
42CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) 42CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV)
43CFLAGS += -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' -DBINDIR='"$(bindir)"' 43CFLAGS += -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' -DBINDIR='"$(bindir)"' -DVARDIR='"/var/lib/firejail"'
44MANFLAGS = $(HAVE_LTS) $(HAVE_OUTPUT) $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_USERTMPFS) $(HAVE_DBUSPROXY) $(HAVE_FIRETUNNEL) $(HAVE_GLOBALCFG) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_FILE_TRANSFER) $(HAVE_SELINUX) $(HAVE_SUID) $(HAVE_FORCE_NONEWPRIVS) 44MANFLAGS = $(HAVE_LTS) $(HAVE_OUTPUT) $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_USERTMPFS) $(HAVE_DBUSPROXY) $(HAVE_FIRETUNNEL) $(HAVE_GLOBALCFG) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_FILE_TRANSFER) $(HAVE_SELINUX) $(HAVE_SUID) $(HAVE_FORCE_NONEWPRIVS)
45CFLAGS += $(MANFLAGS) 45CFLAGS += $(MANFLAGS)
46CFLAGS += -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -Wformat -Wformat-security 46CFLAGS += -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -Wformat -Wformat-security
diff --git a/src/fids/Makefile.in b/src/fids/Makefile.in
new file mode 100644
index 000000000..5530bcee2
--- /dev/null
+++ b/src/fids/Makefile.in
@@ -0,0 +1,18 @@
1.PHONY: all
2all: fids
3
4include ../common.mk
5
6%.o : %.c $(H_FILE_LIST) ../include/common.h
7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
8
9#fseccomp: $(OBJS) ../lib/common.o ../lib/errno.o ../lib/syscall.o
10fids: $(OBJS)
11 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS)
12
13.PHONY: clean
14clean:; rm -fr *.o fids *.gcov *.gcda *.gcno *.plist
15
16.PHONY: distclean
17distclean: clean
18 rm -fr Makefile
diff --git a/src/fids/blake2b.c b/src/fids/blake2b.c
new file mode 100644
index 000000000..f2aa5ae66
--- /dev/null
+++ b/src/fids/blake2b.c
@@ -0,0 +1,176 @@
1/*
2 * Copyright (C) 2014-2021 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
21/* A simple unkeyed BLAKE2b Implementation based on the official reference
22 * from https://github.com/BLAKE2/BLAKE2.
23 *
24 * The original code was released under CC0 1.0 Universal license (Creative Commons),
25 * a public domain license.
26 */
27
28#include "fids.h"
29
30// little-endian vs big-endian is irrelevant since the checksum is calculated and checked on the same computer.
31static inline uint64_t load64( const void *src ) {
32 uint64_t w;
33 memcpy( &w, src, sizeof( w ) );
34 return w;
35}
36
37// mixing function
38#define ROTR64(x, y) (((x) >> (y)) ^ ((x) << (64 - (y))))
39#define G(a, b, c, d, x, y) { \
40 v[a] = v[a] + v[b] + x; \
41 v[d] = ROTR64(v[d] ^ v[a], 32); \
42 v[c] = v[c] + v[d]; \
43 v[b] = ROTR64(v[b] ^ v[c], 24); \
44 v[a] = v[a] + v[b] + y; \
45 v[d] = ROTR64(v[d] ^ v[a], 16); \
46 v[c] = v[c] + v[d]; \
47 v[b] = ROTR64(v[b] ^ v[c], 63); }
48
49// init vector
50static const uint64_t iv[8] = {
51 0x6A09E667F3BCC908, 0xBB67AE8584CAA73B,
52 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1,
53 0x510E527FADE682D1, 0x9B05688C2B3E6C1F,
54 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179
55};
56
57
58const uint8_t sigma[12][16] = {
59 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
60 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
61 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
62 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
63 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
64 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
65 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
66 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
67 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
68 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
69 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
70 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
71};
72
73// blake2b context
74typedef struct {
75 uint8_t b[128]; // input buffer
76 uint64_t h[8]; // chained state
77 uint64_t t[2]; // total number of bytes
78 size_t c; // pointer for b[]
79 size_t outlen; // digest size
80} CTX;
81
82// compress function
83static void compress(CTX *ctx, int last) {
84 uint64_t m[16];
85 uint64_t v[16];
86 size_t i;
87
88 for (i = 0; i < 16; i++)
89 m[i] = load64(&ctx->b[8 * i]);
90
91 for (i = 0; i < 8; i++) {
92 v[i] = ctx->h[i];
93 v[i + 8] = iv[i];
94 }
95
96 v[12] ^= ctx->t[0];
97 v[13] ^= ctx->t[1];
98 if (last)
99 v[14] = ~v[14];
100
101 for (i = 0; i < 12; i++) {
102 G( 0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]);
103 G( 1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]);
104 G( 2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]);
105 G( 3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]);
106 G( 0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]);
107 G( 1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]);
108 G( 2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]);
109 G( 3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]);
110 }
111
112 for( i = 0; i < 8; ++i )
113 ctx->h[i] ^= v[i] ^ v[i + 8];
114}
115
116static int init(CTX *ctx, size_t outlen) { // (keylen=0: no key)
117 size_t i;
118
119 if (outlen == 0 || outlen > 64)
120 return -1;
121
122 for (i = 0; i < 8; i++)
123 ctx->h[i] = iv[i];
124 ctx->h[0] ^= 0x01010000 ^ outlen;
125
126 ctx->t[0] = 0;
127 ctx->t[1] = 0;
128 ctx->c = 0;
129 ctx->outlen = outlen;
130
131 return 0;
132}
133
134static void update(CTX *ctx, const void *in, size_t inlen) {
135 size_t i;
136
137 for (i = 0; i < inlen; i++) {
138 if (ctx->c == 128) {
139 ctx->t[0] += ctx->c;
140 if (ctx->t[0] < ctx->c)
141 ctx->t[1]++;
142 compress(ctx, 0);
143 ctx->c = 0;
144 }
145 ctx->b[ctx->c++] = ((const uint8_t *) in)[i];
146 }
147}
148
149static void final(CTX *ctx, void *out) {
150 size_t i;
151
152 ctx->t[0] += ctx->c;
153 if (ctx->t[0] < ctx->c)
154 ctx->t[1]++;
155
156 while (ctx->c < 128)
157 ctx->b[ctx->c++] = 0;
158 compress(ctx, 1);
159
160 for (i = 0; i < ctx->outlen; i++) {
161 ((uint8_t *) out)[i] =
162 (ctx->h[i >> 3] >> (8 * (i & 7))) & 0xFF;
163 }
164}
165
166// public function
167int blake2b(void *out, size_t outlen, const void *in, size_t inlen) {
168 CTX ctx;
169
170 if (init(&ctx, outlen))
171 return -1;
172 update(&ctx, in, inlen);
173 final(&ctx, out);
174
175 return 0;
176}
diff --git a/src/fids/config b/src/fids/config
new file mode 100644
index 000000000..c18c97260
--- /dev/null
+++ b/src/fids/config
@@ -0,0 +1,16 @@
1/bin
2/sbin
3/usr/bin
4/usr/sbin
5/usr/games
6/opt
7/usr/share/ca-certificates
8
9
10/home/netblue/.bashrc
11/home/netblue/.config/firejail
12/home/netblue/.config/autostart
13/home/netblue/Desktop/*.desktop
14/home/netblue/.ssh
15/home/netblue/.gnupg
16
diff --git a/src/fids/db.c b/src/fids/db.c
new file mode 100644
index 000000000..35caf7eeb
--- /dev/null
+++ b/src/fids/db.c
@@ -0,0 +1,158 @@
1/*
2 * Copyright (C) 2014-2021 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"fids.h"
21
22typedef struct db_t {
23 struct db_t *next;
24 char *fname;
25 char *checksum;
26 char *mode;
27 int checked;
28} DB;
29
30#define MAXBUF 4096
31static DB *database[HASH_MAX] = {NULL};
32
33// djb2 hash function by Dan Bernstein
34static unsigned hash(const char *str) {
35 unsigned long hash = 5381;
36 int c;
37
38 while ((c = *str++) != '\0')
39 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
40
41 return hash & (HASH_MAX - 1);
42}
43
44#if 0
45// for testing the hash table
46static void db_print(void) {
47 int i;
48 for (i = 0; i < HASH_MAX; i++) {
49 int cnt = 0;
50 DB *ptr = database[i];
51 while (ptr) {
52 cnt++;
53 ptr = ptr->next;
54 }
55 printf("%d ", cnt);
56 fflush(0);
57 }
58 printf("\n");
59}
60#endif
61
62static void db_add(const char *fname, const char *checksum, const char *mode) {
63 DB *ptr = malloc(sizeof(DB));
64 if (!ptr)
65 errExit("malloc");
66 ptr->fname = strdup(fname);
67 ptr->checksum = strdup(checksum);
68 ptr->mode = strdup(mode);
69 ptr->checked = 0;
70 if (!ptr->fname || !ptr->checksum || !ptr->mode)
71 errExit("strdup");
72
73 unsigned h = hash(fname);
74 ptr->next = database[h];
75 database[h] = ptr;
76}
77
78void db_check(const char *fname, const char *checksum, const char *mode) {
79 assert(fname);
80 assert(checksum);
81 assert(mode);
82
83 unsigned h =hash(fname);
84 DB *ptr = database[h];
85 while (ptr) {
86 if (strcmp(fname, ptr->fname) == 0) {
87 ptr->checked = 1;
88 break;
89 }
90 ptr = ptr->next;
91 }
92
93 if (ptr ) {
94 if (strcmp(checksum, ptr->checksum)) {
95 f_modified++;
96 fprintf(stderr, "\nWarning: modified %s\n", fname);
97 }
98 if (strcmp(mode, ptr->mode)) {
99 f_permissions++;
100 fprintf(stderr, "\nWarning: permissions %s: old %s, new %s\n",
101 fname, ptr->mode, mode);
102 }
103 }
104 else {
105 f_new++;
106 fprintf(stderr, "\nWarning: new file %s\n", fname);
107 }
108}
109
110void db_missing(void) {
111 int i;
112 for (i = 0; i < HASH_MAX; i++) {
113 DB *ptr = database[i];
114 while (ptr) {
115 if (!ptr->checked) {
116 f_removed++;
117 fprintf(stderr, "Warning: removed %s\n", ptr->fname);
118 }
119 ptr = ptr->next;
120 }
121 }
122}
123
124// return 0 if ok, 1 if error
125int db_init(void) {
126 char buf[MAXBUF];
127 while(fgets(buf, MAXBUF, stdin)) {
128 // split - tab separated
129
130 char *mode = buf;
131 char *ptr = strchr(buf, '\t');
132 if (!ptr)
133 goto errexit;
134 *ptr = '\0';
135
136 char *checksum = ptr + 1;
137 ptr = strchr(checksum, '\t');
138 if (!ptr)
139 goto errexit;
140 *ptr = '\0';
141
142 char *fname = ptr + 1;
143 ptr = strchr(fname, '\n');
144 if (!ptr)
145 goto errexit;
146 *ptr = '\0';
147
148 db_add(fname, checksum, mode);
149 }
150// db_print();
151
152 return 0;
153
154errexit:
155 fprintf(stderr, "Error fids: database corrupted\n");
156 exit(1);
157}
158
diff --git a/src/fids/db_exclude.c b/src/fids/db_exclude.c
new file mode 100644
index 000000000..994e6f9df
--- /dev/null
+++ b/src/fids/db_exclude.c
@@ -0,0 +1,56 @@
1/*
2 * Copyright (C) 2014-2021 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"fids.h"
21
22typedef struct db_exclude_t {
23 struct db_exclude_t *next;
24 char *fname;
25 int len;
26} DB_EXCLUDE;
27static DB_EXCLUDE *database = NULL;
28
29void db_exclude_add(const char *fname) {
30 assert(fname);
31
32 DB_EXCLUDE *ptr = malloc(sizeof(DB_EXCLUDE));
33 if (!ptr)
34 errExit("malloc");
35
36 ptr->fname = strdup(fname);
37 if (!ptr->fname)
38 errExit("strdup");
39 ptr->len = strlen(fname);
40 ptr->next = database;
41 database = ptr;
42}
43
44int db_exclude_check(const char *fname) {
45 assert(fname);
46
47 DB_EXCLUDE *ptr = database;
48 while (ptr != NULL) {
49 if (strncmp(fname, ptr->fname, ptr->len) == 0)
50 return 1;
51 ptr = ptr->next;
52 }
53
54 return 0;
55}
56
diff --git a/src/fids/fids.h b/src/fids/fids.h
new file mode 100644
index 000000000..a2e2886fe
--- /dev/null
+++ b/src/fids/fids.h
@@ -0,0 +1,51 @@
1/*
2 * Copyright (C) 2014-2021 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#ifndef FIDS_H
21#define FIDS_H
22
23#include "../include/common.h"
24
25// main.c
26#define MAX_DIR_LEVEL 20 // max directory tree depth
27#define MAX_INCLUDE_LEVEL 10 // max include level for config files
28extern int f_scanned;
29extern int f_modified;
30extern int f_new;
31extern int f_removed;
32extern int f_permissions;
33
34// db.c
35#define HASH_MAX 2048 // power of 2
36int db_init(void);
37void db_check(const char *fname, const char *checksum, const char *mode);
38void db_missing(void);
39
40// db_exclude.c
41void db_exclude_add(const char *fname);
42int db_exclude_check(const char *fname);
43
44
45// blake2b.c
46//#define KEY_SIZE 128 // key size in bytes
47#define KEY_SIZE 256
48//#define KEY_SIZE 512
49int blake2b(void *out, size_t outlen, const void *in, size_t inlen);
50
51#endif \ No newline at end of file
diff --git a/src/fids/main.c b/src/fids/main.c
new file mode 100644
index 000000000..c899b55e1
--- /dev/null
+++ b/src/fids/main.c
@@ -0,0 +1,371 @@
1/*
2 * Copyright (C) 2014-2021 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 "fids.h"
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <sys/mman.h>
26#include <dirent.h>
27#include <glob.h>
28
29#define MAXBUF 4096
30
31static int dir_level = 1;
32static int include_level = 0;
33int arg_init = 0;
34int arg_check = 0;
35char *arg_homedir = NULL;
36char *arg_dbfile = NULL;
37
38int f_scanned = 0;
39int f_modified = 0;
40int f_new = 0;
41int f_removed = 0;
42int f_permissions = 0;
43
44
45
46static inline int is_dir(const char *fname) {
47 assert(fname);
48
49 struct stat s;
50 if (stat(fname, &s) == 0) {
51 if (S_ISDIR(s.st_mode))
52 return 1;
53 }
54 return 0;
55}
56
57static inline int is_link(const char *fname) {
58 assert(fname);
59
60 char c;
61 ssize_t rv = readlink(fname, &c, 1);
62 return (rv != -1);
63}
64
65// mode is an array of 10 chars or more
66static inline void file_mode(const char *fname, char *mode) {
67 assert(fname);
68 assert(mode);
69
70 struct stat s;
71 if (stat(fname, &s)) {
72 *mode = '\0';
73 return;
74 }
75
76 sprintf(mode, (s.st_mode & S_IRUSR) ? "r" : "-");
77 sprintf(mode + 1, (s.st_mode & S_IWUSR) ? "w" : "-");
78 sprintf(mode + 2, (s.st_mode & S_IXUSR) ? "x" : "-");
79 sprintf(mode + 3, (s.st_mode & S_IRGRP) ? "r" : "-");
80 sprintf(mode + 4, (s.st_mode & S_IWGRP) ? "w" : "-");
81 sprintf(mode + 5, (s.st_mode & S_IXGRP) ? "x" : "-");
82 sprintf(mode + 6, (s.st_mode & S_IROTH) ? "r" : "-");
83 sprintf(mode + 7, (s.st_mode & S_IWOTH) ? "w" : "-");
84 sprintf(mode + 8, (s.st_mode & S_IXOTH) ? "x" : "-");
85}
86
87
88static void file_checksum(const char *fname) {
89 assert(fname);
90
91 int fd = open(fname, O_RDONLY);
92 if (fd == -1)
93 return;
94
95 off_t size = lseek(fd, 0, SEEK_END);
96 if (size < 0) {
97 close(fd);
98 return;
99 }
100
101 char *content = "empty";
102 int mmapped = 0;
103 if (size == 0) {
104 // empty files don't mmap - use "empty" string as the file content
105 size = 6; // strlen("empty") + 1
106 }
107 else {
108 content = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
109 close(fd);
110 mmapped = 1;
111 }
112
113 unsigned char checksum[KEY_SIZE / 8];
114 blake2b(checksum, sizeof(checksum), content, size);
115 if (mmapped)
116 munmap(content, size);
117
118 // calculate blake2 checksum
119 char str_checksum[(KEY_SIZE / 8) * 2 + 1];
120 int long unsigned i;
121 char *ptr = str_checksum;
122 for (i = 0; i < sizeof(checksum); i++, ptr += 2)
123 sprintf(ptr, "%02x", (unsigned char ) checksum[i]);
124
125 // build permissions string
126 char mode[10];
127 file_mode(fname, mode);
128
129 if (arg_init)
130 printf("%s\t%s\t%s\n", mode, str_checksum, fname);
131 else if (arg_check)
132 db_check(fname, str_checksum, mode);
133 else
134 assert(0);
135
136 f_scanned++;
137 if (f_scanned % 500 == 0)
138 fprintf(stderr, "%d ", f_scanned);
139 fflush(0);
140}
141
142void list_directory(const char *fname) {
143 assert(fname);
144 if (dir_level > MAX_DIR_LEVEL) {
145 fprintf(stderr, "Warning fids: maximum depth level exceeded for %s\n", fname);
146 return;
147 }
148
149 if (db_exclude_check(fname))
150 return;
151
152 if (is_link(fname))
153 return;
154
155 if (!is_dir(fname)) {
156 file_checksum(fname);
157 return;
158 }
159
160 DIR *dir;
161 struct dirent *entry;
162
163 if (!(dir = opendir(fname)))
164 return;
165
166 dir_level++;
167 while ((entry = readdir(dir)) != NULL) {
168 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
169 continue;
170 char *path;
171 if (asprintf(&path, "%s/%s", fname, entry->d_name) == -1)
172 errExit("asprintf");
173 list_directory(path);
174 free(path);
175 }
176 closedir(dir);
177 dir_level--;
178}
179
180void globbing(const char *fname) {
181 assert(fname);
182
183 // filter top directory
184 if (strcmp(fname, "/") == 0)
185 return;
186
187 glob_t globbuf;
188 int globerr = glob(fname, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf);
189 if (globerr) {
190 fprintf(stderr, "Error fids: failed to glob pattern %s\n", fname);
191 exit(1);
192 }
193
194 long unsigned i;
195 for (i = 0; i < globbuf.gl_pathc; i++) {
196 char *path = globbuf.gl_pathv[i];
197 assert(path);
198
199 list_directory(path);
200 }
201
202 globfree(&globbuf);
203}
204
205static void process_config(const char *fname) {
206 assert(fname);
207
208 if (++include_level >= MAX_INCLUDE_LEVEL) {
209 fprintf(stderr, "Error ids: maximum include level for config files exceeded\n");
210 exit(1);
211 }
212
213 // make sure the file is owned by root
214 struct stat s;
215 if (stat(fname, &s)) {
216 if (include_level == 1) {
217 fprintf(stderr, "Error ids: config file not found\n");
218 exit(1);
219 }
220 return;
221 }
222 if (s.st_uid || s.st_gid) {
223 fprintf(stderr, "Error ids: config file not owned by root\n");
224 exit(1);
225 }
226
227 fprintf(stderr, "Loading %s config file\n", fname);
228 FILE *fp = fopen(fname, "r");
229 if (!fp) {
230 fprintf(stderr, "Error fids: cannot open config file %s\n", fname);
231 exit(1);
232 }
233
234 char buf[MAXBUF];
235 int line = 0;
236 while (fgets(buf, MAXBUF, fp)) {
237 line++;
238
239 // trim \n
240 char *ptr = strchr(buf, '\n');
241 if (ptr)
242 *ptr = '\0';
243
244 // comments
245 ptr = strchr(buf, '#');
246 if (ptr)
247 *ptr = '\0';
248
249 // empty space
250 ptr = buf;
251 while (*ptr == ' ' || *ptr == '\t')
252 ptr++;
253 char *start = ptr;
254
255 // empty line
256 if (*start == '\0')
257 continue;
258
259 // trailing spaces
260 ptr = start + strlen(start);
261 ptr--;
262 while (*ptr == ' ' || *ptr == '\t')
263 *ptr-- = '\0';
264
265 // replace ${HOME}
266 if (strncmp(start, "include", 7) == 0) {
267 ptr = start + 7;
268 if ((*ptr != ' ' && *ptr != '\t') || *ptr == '\0') {
269 fprintf(stderr, "Error fids: invalid line %d in %s\n", line, fname);
270 exit(1);
271 }
272 while (*ptr == ' ' || *ptr == '\t')
273 ptr++;
274
275 if (*ptr == '/')
276 process_config(ptr);
277 else {
278 // assume the file is in /etc/firejail
279 char *tmp;
280 if (asprintf(&tmp, "/etc/firejail/%s", ptr) == -1)
281 errExit("asprintf");
282 process_config(tmp);
283 free(tmp);
284 }
285 }
286 else if (*start == '!') {
287 // exclude file or dir
288 start++;
289 if (strncmp(start, "${HOME}", 7))
290 db_exclude_add(start);
291 else {
292 char *fname;
293 if (asprintf(&fname, "%s%s", arg_homedir, start + 7) == -1)
294 errExit("asprintf");
295 db_exclude_add(fname);
296 free(fname);
297 }
298 }
299 else if (strncmp(start, "${HOME}", 7))
300 globbing(start);
301 else {
302 char *fname;
303 if (asprintf(&fname, "%s%s", arg_homedir, start + 7) == -1)
304 errExit("asprintf");
305 globbing(fname);
306 free(fname);
307 }
308 }
309
310 fclose(fp);
311 include_level--;
312}
313
314
315
316void usage(void) {
317 printf("Usage: fids [--help|-h|-?] --init|--check homedir\n");
318}
319
320int main(int argc, char **argv) {
321 int i;
322 for (i = 1; i < argc; i++) {
323 if (strcmp(argv[i], "-h") == 0 ||
324 strcmp(argv[i], "-?") == 0 ||
325 strcmp(argv[i], "--help") == 0) {
326 usage();
327 return 0;
328 }
329 else if (strcmp(argv[i], "--init") == 0)
330 arg_init = 1;
331 else if (strcmp(argv[i], "--check") == 0)
332 arg_check = 1;
333 else if (strncmp(argv[i], "--", 2) == 0) {
334 fprintf(stderr, "Error fids: invalid argument %s\n", argv[i]);
335 exit(1);
336 }
337 }
338
339 if (argc != 3) {
340 fprintf(stderr, "Error fids: invalid number of arguments\n");
341 exit(1);
342 }
343 arg_homedir = argv[2];
344
345 int op = arg_check + arg_init;
346 if (op == 0 || op == 2) {
347 fprintf(stderr, "Error fids: use either --init or --check\n");
348 exit(1);
349 }
350
351 if (arg_init) {
352 process_config(SYSCONFDIR"/ids.config");
353 fprintf(stderr, "\n%d files scanned\n", f_scanned);
354 fprintf(stderr, "IDS database initialized\n");
355 }
356 else if (arg_check) {
357 if (db_init()) {
358 fprintf(stderr, "Error: IDS database not initialized, please run \"firejail --ids-init\"\n");
359 exit(1);
360 }
361
362 process_config(SYSCONFDIR"/ids.config");
363 fprintf(stderr, "\n%d files scanned: modified %d, permissions %d, new %d, removed %d\n",
364 f_scanned, f_modified, f_permissions, f_new, f_removed);
365 db_missing();
366 }
367 else
368 assert(0);
369
370 return 0;
371}
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config
index 3b0ad0aed..046cb209a 100644
--- a/src/firecfg/firecfg.config
+++ b/src/firecfg/firecfg.config
@@ -567,6 +567,7 @@ mypaint
567mypaint-ora-thumbnailer 567mypaint-ora-thumbnailer
568natron 568natron
569ncdu 569ncdu
570ncdu2
570neochat 571neochat
571neomutt 572neomutt
572netactview 573netactview
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 545573c08..2a7d88575 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -835,7 +835,6 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc,
835#define PATH_FNET_MAIN (LIBDIR "/firejail/fnet") // when called from main thread 835#define PATH_FNET_MAIN (LIBDIR "/firejail/fnet") // when called from main thread
836#define PATH_FNET (RUN_FIREJAIL_LIB_DIR "/fnet") // when called from sandbox thread 836#define PATH_FNET (RUN_FIREJAIL_LIB_DIR "/fnet") // when called from sandbox thread
837 837
838//#define PATH_FNETFILTER (LIBDIR "/firejail/fnetfilter")
839#define PATH_FNETFILTER (RUN_FIREJAIL_LIB_DIR "/fnetfilter") 838#define PATH_FNETFILTER (RUN_FIREJAIL_LIB_DIR "/fnetfilter")
840 839
841#define PATH_FIREMON (PREFIX "/bin/firemon") 840#define PATH_FIREMON (PREFIX "/bin/firemon")
@@ -848,17 +847,16 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc,
848// it is also run from inside the sandbox by --debug; in this case we do an access(filename, X_OK) test first 847// it is also run from inside the sandbox by --debug; in this case we do an access(filename, X_OK) test first
849#define PATH_FSEC_PRINT (LIBDIR "/firejail/fsec-print") 848#define PATH_FSEC_PRINT (LIBDIR "/firejail/fsec-print")
850 849
851//#define PATH_FSEC_OPTIMIZE (LIBDIR "/firejail/fsec-optimize")
852#define PATH_FSEC_OPTIMIZE (RUN_FIREJAIL_LIB_DIR "/fsec-optimize") 850#define PATH_FSEC_OPTIMIZE (RUN_FIREJAIL_LIB_DIR "/fsec-optimize")
853 851
854//#define PATH_FCOPY (LIBDIR "/firejail/fcopy")
855#define PATH_FCOPY (RUN_FIREJAIL_LIB_DIR "/fcopy") 852#define PATH_FCOPY (RUN_FIREJAIL_LIB_DIR "/fcopy")
856 853
857#define SBOX_STDIN_FILE "/run/firejail/mnt/sbox_stdin" 854#define SBOX_STDIN_FILE "/run/firejail/mnt/sbox_stdin"
858 855
859//#define PATH_FLDD (LIBDIR "/firejail/fldd")
860#define PATH_FLDD (RUN_FIREJAIL_LIB_DIR "/fldd") 856#define PATH_FLDD (RUN_FIREJAIL_LIB_DIR "/fldd")
861 857
858#define PATH_FIDS (LIBDIR "/firejail/fids")
859
862// bitmapped filters for sbox_run 860// bitmapped filters for sbox_run
863#define SBOX_ROOT (1 << 0) // run the sandbox as root 861#define SBOX_ROOT (1 << 0) // run the sandbox as root
864#define SBOX_USER (1 << 1) // run the sandbox as a regular user 862#define SBOX_USER (1 << 1) // run the sandbox as a regular user
@@ -903,4 +901,7 @@ void dhcp_start(void);
903// selinux.c 901// selinux.c
904void selinux_relabel_path(const char *path, const char *inside_path); 902void selinux_relabel_path(const char *path, const char *inside_path);
905 903
904// ids.c
905void run_ids(int argc, char **argv);
906
906#endif 907#endif
diff --git a/src/firejail/ids.c b/src/firejail/ids.c
new file mode 100644
index 000000000..59acdb1fe
--- /dev/null
+++ b/src/firejail/ids.c
@@ -0,0 +1,89 @@
1/*
2 * Copyright (C) 2014-2021 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 "firejail.h"
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <fcntl.h>
24
25
26static void ids_init(void) {
27 // store checksums as root in /var/lib/firejail/${USERNAME}.ids
28 char *fname;
29 if (asprintf(&fname, VARDIR"/%s.ids", cfg.username) == -1)
30 errExit("asprintf");
31
32 int rv = unlink(fname);
33 (void) rv;
34 int fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0600);
35 if (fd < 0) {
36 fprintf(stderr, "Error: cannot create %s\n", fname);
37 exit(1);
38 }
39
40 // redirect output
41 close(STDOUT_FILENO);
42 if (dup(fd) != STDOUT_FILENO)
43 errExit("dup");
44 close(fd);
45
46 sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FIDS, "--init", cfg.homedir);
47}
48
49static void ids_check(void) {
50 // store checksums as root in /var/lib/firejail/${USERNAME}.ids
51 char *fname;
52 if (asprintf(&fname, VARDIR"/%s.ids", cfg.username) == -1)
53 errExit("asprintf");
54
55 int fd = open(fname, O_RDONLY);
56 if (fd < 0) {
57 fprintf(stderr, "Error: cannot open %s\n", fname);
58 exit(1);
59 }
60
61 // redirect input
62 close(STDIN_FILENO);
63 if (dup(fd) != STDIN_FILENO)
64 errExit("dup");
65 close(fd);
66
67 sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP| SBOX_ALLOW_STDIN, 3, PATH_FIDS, "--check", cfg.homedir);
68}
69
70void run_ids(int argc, char **argv) {
71 if (argc != 2) {
72 fprintf(stderr, "Error: only one IDS command expected\n");
73 exit(1);
74 }
75
76 EUID_ROOT();
77 struct stat s;
78 if (stat(VARDIR, &s)) // /var/lib/firejail
79 create_empty_dir_as_root(VARDIR, 0700);
80
81 if (strcmp(argv[1], "--ids-init") == 0)
82 ids_init();
83 else if (strcmp(argv[1], "--ids-check") == 0)
84 ids_check();
85 else
86 fprintf(stderr, "Error: unrecognized IDS command\n");
87
88 exit(0);
89} \ No newline at end of file
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 655e6e9d0..ef3bf8bf5 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -1068,6 +1068,10 @@ int main(int argc, char **argv, char **envp) {
1068 if (check_arg(argc, argv, "--build", 0)) // supports both --build and --build=filename 1068 if (check_arg(argc, argv, "--build", 0)) // supports both --build and --build=filename
1069 run_builder(argc, argv); // this function will not return 1069 run_builder(argc, argv); // this function will not return
1070 1070
1071 // intrusion detection system
1072 if (check_arg(argc, argv, "--ids-", 0)) // supports both --ids-init and --ids-check
1073 run_ids(argc, argv); // this function will not return
1074
1071 EUID_ROOT(); 1075 EUID_ROOT();
1072#ifndef HAVE_SUID 1076#ifndef HAVE_SUID
1073 if (geteuid() != 0) { 1077 if (geteuid() != 0) {
diff --git a/src/firejail/usage.c b/src/firejail/usage.c
index b4f3021c7..d843c74ae 100644
--- a/src/firejail/usage.c
+++ b/src/firejail/usage.c
@@ -98,6 +98,8 @@ static char *usage_str =
98 " --help, -? - this help screen.\n" 98 " --help, -? - this help screen.\n"
99 " --hostname=name - set sandbox hostname.\n" 99 " --hostname=name - set sandbox hostname.\n"
100 " --hosts-file=file - use file as /etc/hosts.\n" 100 " --hosts-file=file - use file as /etc/hosts.\n"
101 " --ids-check - verify file system.\n"
102 " --ids-init - initialize IDS database.\n"
101 " --ignore=command - ignore command in profile files.\n" 103 " --ignore=command - ignore command in profile files.\n"
102#ifdef HAVE_NETWORK 104#ifdef HAVE_NETWORK
103 " --interface=name - move interface in sandbox.\n" 105 " --interface=name - move interface in sandbox.\n"
diff --git a/src/tools/profcleaner.sh b/src/tools/profcleaner.sh
index 709008e08..96402aed6 100755
--- a/src/tools/profcleaner.sh
+++ b/src/tools/profcleaner.sh
@@ -38,8 +38,8 @@ else
38fi 38fi
39 39
40sed -i -E \ 40sed -i -E \
41 -e "s/^(# |#)?blacklist/\1deny/" \ 41 -e "s/^(# |#)?(ignore )?blacklist/\1\2deny/" \
42 -e "s/^(# |#)?noblacklist/\1nodeny/" \ 42 -e "s/^(# |#)?(ignore )?noblacklist/\1\2nodeny/" \
43 -e "s/^(# |#)?whitelist/\1allow/" \ 43 -e "s/^(# |#)?(ignore )?whitelist/\1\2allow/" \
44 -e "s/^(# |#)?nowhitelist/\1noallow/" \ 44 -e "s/^(# |#)?(ignore )?nowhitelist/\1\2noallow/" \
45 "${profiles[@]}" 45 "${profiles[@]}"