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.c159
-rw-r--r--src/fids/db_exclude.c56
-rw-r--r--src/fids/fids.h51
-rw-r--r--src/fids/main.c370
-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
12 files changed, 947 insertions, 5 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..e0021bc48
--- /dev/null
+++ b/src/fids/db.c
@@ -0,0 +1,159 @@
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 const unsigned char *s = (unsigned char *) str;
36 unsigned long hash = 5381;
37 int c;
38
39 while (c = *s++)
40 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
41
42 return hash & (HASH_MAX - 1);
43}
44
45#if 0
46// for testing the hash table
47static void db_print(void) {
48 int i;
49 for (i = 0; i < HASH_MAX; i++) {
50 int cnt = 0;
51 DB *ptr = database[i];
52 while (ptr) {
53 cnt++;
54 ptr = ptr->next;
55 }
56 printf("%d ", cnt);
57 fflush(0);
58 }
59 printf("\n");
60}
61#endif
62
63static void db_add(const char *fname, const char *checksum, const char *mode) {
64 DB *ptr = malloc(sizeof(DB));
65 if (!ptr)
66 errExit("malloc");
67 ptr->fname = strdup(fname);
68 ptr->checksum = strdup(checksum);
69 ptr->mode = strdup(mode);
70 ptr->checked = 0;
71 if (!ptr->fname || !ptr->checksum || !ptr->mode)
72 errExit("strdup");
73
74 unsigned h = hash(fname);
75 ptr->next = database[h];
76 database[h] = ptr;
77}
78
79void db_check(const char *fname, const char *checksum, const char *mode) {
80 assert(fname);
81 assert(checksum);
82 assert(mode);
83
84 unsigned h =hash(fname);
85 DB *ptr = database[h];
86 while (ptr) {
87 if (strcmp(fname, ptr->fname) == 0) {
88 ptr->checked = 1;
89 break;
90 }
91 ptr = ptr->next;
92 }
93
94 if (ptr ) {
95 if (strcmp(checksum, ptr->checksum)) {
96 f_modified++;
97 fprintf(stderr, "\nWarning: modified %s\n", fname);
98 }
99 if (strcmp(mode, ptr->mode)) {
100 f_permissions++;
101 fprintf(stderr, "\nWarning: permissions %s: old %s, new %s\n",
102 fname, ptr->mode, mode);
103 }
104 }
105 else {
106 f_new++;
107 fprintf(stderr, "\nWarning: new file %s\n", fname);
108 }
109}
110
111void db_missing(void) {
112 int i;
113 for (i = 0; i < HASH_MAX; i++) {
114 DB *ptr = database[i];
115 while (ptr) {
116 if (!ptr->checked) {
117 f_removed++;
118 fprintf(stderr, "Warning: removed %s\n", ptr->fname);
119 }
120 ptr = ptr->next;
121 }
122 }
123}
124
125// return 0 if ok, 1 if error
126int db_init(void) {
127 char buf[MAXBUF];
128 while(fgets(buf, MAXBUF, stdin)) {
129 // split - tab separated
130
131 char *mode = buf;
132 char *ptr = strchr(buf, '\t');
133 if (!ptr)
134 goto errexit;
135 *ptr = '\0';
136
137 char *checksum = ptr + 1;
138 ptr = strchr(checksum, '\t');
139 if (!ptr)
140 goto errexit;
141 *ptr = '\0';
142
143 char *fname = ptr + 1;
144 ptr = strchr(fname, '\n');
145 if (!ptr)
146 goto errexit;
147 *ptr = '\0';
148
149 db_add(fname, checksum, mode);
150 }
151// db_print();
152
153 return 0;
154
155errexit:
156 fprintf(stderr, "Error fids: database corrupted\n");
157 exit(1);
158}
159
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..b53a9828e
--- /dev/null
+++ b/src/fids/main.c
@@ -0,0 +1,370 @@
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 int rv = stat(fname, &s);
51 if (S_ISDIR(s.st_mode))
52 return 1;
53 return 0;
54}
55
56static inline int is_link(const char *fname) {
57 assert(fname);
58
59 char c;
60 ssize_t rv = readlink(fname, &c, 1);
61 return (rv != -1);
62}
63
64// mode is an array of 10 chars or more
65static inline void file_mode(const char *fname, char *mode) {
66 assert(fname);
67 assert(mode);
68
69 struct stat s;
70 if (stat(fname, &s)) {
71 *mode = '\0';
72 return;
73 }
74
75 sprintf(mode, (s.st_mode & S_IRUSR) ? "r" : "-");
76 sprintf(mode + 1, (s.st_mode & S_IWUSR) ? "w" : "-");
77 sprintf(mode + 2, (s.st_mode & S_IXUSR) ? "x" : "-");
78 sprintf(mode + 3, (s.st_mode & S_IRGRP) ? "r" : "-");
79 sprintf(mode + 4, (s.st_mode & S_IWGRP) ? "w" : "-");
80 sprintf(mode + 5, (s.st_mode & S_IXGRP) ? "x" : "-");
81 sprintf(mode + 6, (s.st_mode & S_IROTH) ? "r" : "-");
82 sprintf(mode + 7, (s.st_mode & S_IWOTH) ? "w" : "-");
83 sprintf(mode + 8, (s.st_mode & S_IXOTH) ? "x" : "-");
84}
85
86
87static void file_checksum(const char *fname) {
88 assert(fname);
89
90 int fd = open(fname, O_RDONLY);
91 if (fd == -1)
92 return;
93
94 off_t size = lseek(fd, 0, SEEK_END);
95 if (size < 0) {
96 close(fd);
97 return;
98 }
99
100 char *content = "empty";
101 int mmapped = 0;
102 if (size == 0) {
103 // empty files don't mmap - use "empty" string as the file content
104 size = 6; // strlen("empty") + 1
105 }
106 else {
107 content = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
108 close(fd);
109 mmapped = 1;
110 }
111
112 unsigned char checksum[KEY_SIZE / 8];
113 blake2b(checksum, sizeof(checksum), content, size);
114 if (mmapped)
115 munmap(content, size);
116
117 // calculate blake2 checksum
118 char str_checksum[(KEY_SIZE / 8) * 2 + 1];
119 int i;
120 char *ptr = str_checksum;
121 for (i = 0; i < sizeof(checksum); i++, ptr += 2)
122 sprintf(ptr, "%02x", (unsigned char ) checksum[i]);
123
124 // build permissions string
125 char mode[10];
126 file_mode(fname, mode);
127
128 if (arg_init)
129 printf("%s\t%s\t%s\n", mode, str_checksum, fname);
130 else if (arg_check)
131 db_check(fname, str_checksum, mode);
132 else
133 assert(0);
134
135 f_scanned++;
136 if (f_scanned % 500 == 0)
137 fprintf(stderr, "%d ", f_scanned);
138 fflush(0);
139}
140
141void list_directory(const char *fname) {
142 assert(fname);
143 if (dir_level > MAX_DIR_LEVEL) {
144 fprintf(stderr, "Warning fids: maximum depth level exceeded for %s\n", fname);
145 return;
146 }
147
148 if (db_exclude_check(fname))
149 return;
150
151 if (is_link(fname))
152 return;
153
154 if (!is_dir(fname)) {
155 file_checksum(fname);
156 return;
157 }
158
159 DIR *dir;
160 struct dirent *entry;
161
162 if (!(dir = opendir(fname)))
163 return;
164
165 dir_level++;
166 while ((entry = readdir(dir)) != NULL) {
167 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
168 continue;
169 char *path;
170 if (asprintf(&path, "%s/%s", fname, entry->d_name) == -1)
171 errExit("asprintf");
172 list_directory(path);
173 free(path);
174 }
175 closedir(dir);
176 dir_level--;
177}
178
179void globbing(const char *fname) {
180 assert(fname);
181
182 // filter top directory
183 if (strcmp(fname, "/") == 0)
184 return;
185
186 glob_t globbuf;
187 int globerr = glob(fname, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf);
188 if (globerr) {
189 fprintf(stderr, "Error fids: failed to glob pattern %s\n", fname);
190 exit(1);
191 }
192
193 int i;
194 for (i = 0; i < globbuf.gl_pathc; i++) {
195 char *path = globbuf.gl_pathv[i];
196 assert(path);
197
198 list_directory(path);
199 }
200
201 globfree(&globbuf);
202}
203
204static void process_config(const char *fname) {
205 assert(fname);
206
207 if (++include_level >= MAX_INCLUDE_LEVEL) {
208 fprintf(stderr, "Error ids: maximum include level for config files exceeded\n");
209 exit(1);
210 }
211
212 // make sure the file is owned by root
213 struct stat s;
214 if (stat(fname, &s)) {
215 if (include_level == 1) {
216 fprintf(stderr, "Error ids: config file not found\n");
217 exit(1);
218 }
219 return;
220 }
221 if (s.st_uid || s.st_gid) {
222 fprintf(stderr, "Error ids: config file not owned by root\n");
223 exit(1);
224 }
225
226 fprintf(stderr, "Loading %s config file\n", fname);
227 FILE *fp = fopen(fname, "r");
228 if (!fp) {
229 fprintf(stderr, "Error fids: cannot open config file %s\n", fname);
230 exit(1);
231 }
232
233 char buf[MAXBUF];
234 int line = 0;
235 while (fgets(buf, MAXBUF, fp)) {
236 line++;
237
238 // trim \n
239 char *ptr = strchr(buf, '\n');
240 if (ptr)
241 *ptr = '\0';
242
243 // comments
244 ptr = strchr(buf, '#');
245 if (ptr)
246 *ptr = '\0';
247
248 // empty space
249 ptr = buf;
250 while (*ptr == ' ' || *ptr == '\t')
251 ptr++;
252 char *start = ptr;
253
254 // empty line
255 if (*start == '\0')
256 continue;
257
258 // trailing spaces
259 ptr = start + strlen(start);
260 ptr--;
261 while (*ptr == ' ' || *ptr == '\t')
262 *ptr-- = '\0';
263
264 // replace ${HOME}
265 if (strncmp(start, "include", 7) == 0) {
266 ptr = start + 7;
267 if ((*ptr != ' ' && *ptr != '\t') || *ptr == '\0') {
268 fprintf(stderr, "Error fids: invalid line %d in %s\n", line, fname);
269 exit(1);
270 }
271 while (*ptr == ' ' || *ptr == '\t')
272 ptr++;
273
274 if (*ptr == '/')
275 process_config(ptr);
276 else {
277 // assume the file is in /etc/firejail
278 char *tmp;
279 if (asprintf(&tmp, "/etc/firejail/%s", ptr) == -1)
280 errExit("asprintf");
281 process_config(tmp);
282 free(tmp);
283 }
284 }
285 else if (*start == '!') {
286 // exclude file or dir
287 start++;
288 if (strncmp(start, "${HOME}", 7))
289 db_exclude_add(start);
290 else {
291 char *fname;
292 if (asprintf(&fname, "%s%s", arg_homedir, start + 7) == -1)
293 errExit("asprintf");
294 db_exclude_add(fname);
295 free(fname);
296 }
297 }
298 else if (strncmp(start, "${HOME}", 7))
299 globbing(start);
300 else {
301 char *fname;
302 if (asprintf(&fname, "%s%s", arg_homedir, start + 7) == -1)
303 errExit("asprintf");
304 globbing(fname);
305 free(fname);
306 }
307 }
308
309 fclose(fp);
310 include_level--;
311}
312
313
314
315void usage(void) {
316 printf("Usage: fids [--help|-h|-?] --init|--check homedir\n");
317}
318
319int main(int argc, char **argv) {
320 int i;
321 for (i = 1; i < argc; i++) {
322 if (strcmp(argv[i], "-h") == 0 ||
323 strcmp(argv[i], "-?") == 0 ||
324 strcmp(argv[i], "--help") == 0) {
325 usage();
326 return 0;
327 }
328 else if (strcmp(argv[i], "--init") == 0)
329 arg_init = 1;
330 else if (strcmp(argv[i], "--check") == 0)
331 arg_check = 1;
332 else if (strncmp(argv[i], "--", 2) == 0) {
333 fprintf(stderr, "Error fids: invalid argument %s\n", argv[i]);
334 exit(1);
335 }
336 }
337
338 if (argc != 3) {
339 fprintf(stderr, "Error fids: invalid number of arguments\n");
340 exit(1);
341 }
342 arg_homedir = argv[2];
343
344 int op = arg_check + arg_init;
345 if (op == 0 || op == 2) {
346 fprintf(stderr, "Error fids: use either --init or --check\n");
347 exit(1);
348 }
349
350 if (arg_init) {
351 process_config(SYSCONFDIR"/ids.config");
352 fprintf(stderr, "\n%d files scanned\n", f_scanned);
353 fprintf(stderr, "IDS database initialized\n");
354 }
355 else if (arg_check) {
356 if (db_init()) {
357 fprintf(stderr, "Error: IDS database not initialized, please run \"firejail --ids-init\"\n");
358 exit(1);
359 }
360
361 process_config(SYSCONFDIR"/ids.config");
362 fprintf(stderr, "\n%d files scanned: modified %d, permissions %d, new %d, removed %d\n",
363 f_scanned, f_modified, f_permissions, f_new, f_removed);
364 db_missing();
365 }
366 else
367 assert(0);
368
369 return 0;
370}
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"