aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/bash_completion/firejail.bash_completion6
-rw-r--r--src/firejail/firejail.h7
-rw-r--r--src/firejail/fs.c13
-rw-r--r--src/firejail/fs_home.c16
-rw-r--r--src/firejail/fs_whitelist.c205
-rw-r--r--src/firejail/main.c9
-rw-r--r--src/firejail/profile.c4
-rw-r--r--src/firejail/sandbox.c10
-rw-r--r--src/firejail/usage.c1
-rw-r--r--src/man/firejail-login.txt4
-rw-r--r--src/man/firejail.txt13
11 files changed, 263 insertions, 25 deletions
diff --git a/src/bash_completion/firejail.bash_completion b/src/bash_completion/firejail.bash_completion
index 50eccf536..98ca5e7a4 100644
--- a/src/bash_completion/firejail.bash_completion
+++ b/src/bash_completion/firejail.bash_completion
@@ -39,7 +39,11 @@ _firejail()
39 _filedir 39 _filedir
40 return 0 40 return 0
41 ;; 41 ;;
42 --read-only) 42 --whitelist)
43 _filedir
44 return 0
45 ;;
46 --read-only)
43 _filedir 47 _filedir
44 return 0 48 return 0
45 ;; 49 ;;
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 315a8c7f4..116bd404a 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -28,6 +28,7 @@
28#define MNT_DIR "/tmp/firejail/mnt" 28#define MNT_DIR "/tmp/firejail/mnt"
29#define HOME_DIR "/tmp/firejail/mnt/home" 29#define HOME_DIR "/tmp/firejail/mnt/home"
30#define ETC_DIR "/tmp/firejail/mnt/etc" 30#define ETC_DIR "/tmp/firejail/mnt/etc"
31#define WHITELIST_HOME_DIR "/tmp/firejail/mnt/whome"
31#define DEFAULT_USER_PROFILE "generic" 32#define DEFAULT_USER_PROFILE "generic"
32#define DEFAULT_ROOT_PROFILE "server" 33#define DEFAULT_ROOT_PROFILE "server"
33#define MAX_INCLUDE_LEVEL 6 34#define MAX_INCLUDE_LEVEL 6
@@ -146,6 +147,7 @@ extern int arg_shell_none; // run the program directly without a shell
146extern int arg_private_dev; // private dev directory 147extern int arg_private_dev; // private dev directory
147extern int arg_private_etc; // private etc directory 148extern int arg_private_etc; // private etc directory
148extern int arg_scan; // arp-scan all interfaces 149extern int arg_scan; // arp-scan all interfaces
150extern int arg_whitelist; // whitelist commad
149 151
150extern int parent_to_child_fds[2]; 152extern int parent_to_child_fds[2];
151extern int child_to_parent_fds[2]; 153extern int child_to_parent_fds[2];
@@ -186,8 +188,7 @@ void fs_build_firejail_dir(void);
186// build /tmp/firejail/mnt directory 188// build /tmp/firejail/mnt directory
187void fs_build_mnt_dir(void); 189void fs_build_mnt_dir(void);
188// blacklist files or directoies by mounting empty files on top of them 190// blacklist files or directoies by mounting empty files on top of them
189void fs_blacklist(const char *homedir); 191void fs_blacklist(void);
190//void fs_blacklist(char **blacklist, const char *homedir);
191// remount a directory read-only 192// remount a directory read-only
192void fs_rdonly(const char *dir); 193void fs_rdonly(const char *dir);
193// mount /proc and /sys directories 194// mount /proc and /sys directories
@@ -366,5 +367,7 @@ void run_no_sandbox(int argc, char **argv);
366void env_store(const char *str); 367void env_store(const char *str);
367void env_apply(void); 368void env_apply(void);
368 369
370// fs_whitelist.c
371
369#endif 372#endif
370 373
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index 14c76a144..e7388a539 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -242,8 +242,11 @@ static void globbing(OPERATION op, const char *pattern, const char *noblacklist[
242 globfree(&globbuf); 242 globfree(&globbuf);
243} 243}
244 244
245
245// blacklist files or directoies by mounting empty files on top of them 246// blacklist files or directoies by mounting empty files on top of them
246void fs_blacklist(const char *homedir) { 247void fs_blacklist(void) {
248 char *homedir = cfg.homedir;
249 assert(homedir);
247 ProfileEntry *entry = cfg.profile; 250 ProfileEntry *entry = cfg.profile;
248 if (!entry) 251 if (!entry)
249 return; 252 return;
@@ -261,7 +264,13 @@ void fs_blacklist(const char *homedir) {
261 OPERATION op = OPERATION_MAX; 264 OPERATION op = OPERATION_MAX;
262 char *ptr; 265 char *ptr;
263 266
264 // process blacklist command 267 // whitelist commands handled by fs_whitelist()
268 if (strncmp(entry->data, "whitelist ", 10) == 0 || *entry->data == '\0') {
269 entry = entry->next;
270 continue;
271 }
272
273 // process bind command
265 if (strncmp(entry->data, "bind ", 5) == 0) { 274 if (strncmp(entry->data, "bind ", 5) == 0) {
266 char *dname1 = entry->data + 5; 275 char *dname1 = entry->data + 5;
267 char *dname2 = split_comma(dname1); 276 char *dname2 = split_comma(dname1);
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 98d62b685..714417867 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -159,7 +159,6 @@ static void copy_xauthority(void) {
159// private mode (--private=homedir): 159// private mode (--private=homedir):
160// mount homedir on top of /home/user, 160// mount homedir on top of /home/user,
161// tmpfs on top of /root in nonroot mode, 161// tmpfs on top of /root in nonroot mode,
162// tmpfs on top of /tmp in root mode,
163// set skel files, 162// set skel files,
164// restore .Xauthority 163// restore .Xauthority
165void fs_private_homedir(void) { 164void fs_private_homedir(void) {
@@ -214,7 +213,6 @@ void fs_private_homedir(void) {
214// private mode (--private): 213// private mode (--private):
215// mount tmpfs over /home/user, 214// mount tmpfs over /home/user,
216// tmpfs on top of /root in nonroot mode, 215// tmpfs on top of /root in nonroot mode,
217// tmpfs on top of /tmp in root mode
218// set skel files, 216// set skel files,
219// restore .Xauthority 217// restore .Xauthority
220void fs_private(void) { 218void fs_private(void) {
@@ -353,20 +351,6 @@ void fs_check_private_dir(void) {
353 } 351 }
354} 352}
355 353
356#if 0
357static int mkpath(char* file_path, mode_t mode) {
358 assert(file_path && *file_path);
359 char* p;
360 for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) {
361 *p='\0';
362 if (mkdir(file_path, mode)==-1) {
363 if (errno!=EEXIST) { *p='/'; return -1; }
364 }
365 *p='/';
366 }
367 return 0;
368}
369#endif
370 354
371static void duplicate(char *name) { 355static void duplicate(char *name) {
372 char *cmd; 356 char *cmd;
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
new file mode 100644
index 000000000..fac08705d
--- /dev/null
+++ b/src/firejail/fs_whitelist.c
@@ -0,0 +1,205 @@
1/*
2 * Copyright (C) 2014, 2015 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/mount.h>
22#include <sys/stat.h>
23#include <linux/limits.h>
24#include <fnmatch.h>
25#include <glob.h>
26#include <dirent.h>
27#include <fcntl.h>
28#include <errno.h>
29
30static int mkpath(const char* path, mode_t mode) {
31 assert(path && *path);
32
33 // work on a copy of the path
34 char *file_path = strdup(path);
35 if (!file_path)
36 errExit("strdup");
37
38 char* p;
39 for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) {
40 *p='\0';
41 if (mkdir(file_path, mode)==-1) {
42 if (errno != EEXIST) {
43 *p='/';
44 free(file_path);
45 return -1;
46 }
47 }
48 else {
49// TODO: set correct directory mode and properties
50 }
51
52 *p='/';
53 }
54
55 free(file_path);
56 return 0;
57}
58
59static void whitelist_path(const char *path) {
60 assert(path);
61
62 // fname needs to start with /home/username
63 if (strncmp(path, cfg.homedir, strlen(cfg.homedir))) {
64 fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path);
65 exit(1);
66 }
67
68 const char *fname = path + strlen(cfg.homedir);
69 if (*fname == '\0') {
70 fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path);
71 exit(1);
72 }
73
74 char *wfile;
75 if (asprintf(&wfile, "%s/%s", WHITELIST_HOME_DIR, fname) == -1)
76 errExit("asprintf");
77
78 // check if the file exists
79 struct stat s;
80 if (stat(wfile, &s) == 0) {
81 if (arg_debug)
82 printf("Whitelisting %s\n", path);
83 }
84 else {
85 if (arg_debug) {
86 fprintf(stderr, "Warning: %s is an invalid file, skipping...\n", path);
87 }
88 return;
89 }
90
91 // create the path if necessary
92 mkpath(path, 0755);
93
94 // process directory
95 if (S_ISDIR(s.st_mode)) {
96 // create directory
97 int rv = mkdir(path, 0755);
98 if (rv == -1)
99 errExit("mkdir");
100
101 }
102
103 // process regular file
104 else {
105 // create an empty file
106 FILE *fp = fopen(path, "w");
107 if (!fp) {
108 fprintf(stderr, "Error: cannot create empty file in home directory\n");
109 exit(1);
110 }
111 fclose(fp);
112 }
113
114 // set file properties
115 if (chown(path, s.st_uid, s.st_gid) < 0)
116 errExit("chown");
117 if (chmod(path, s.st_mode) < 0)
118 errExit("chmod");
119
120 // mount
121 if (mount(wfile, path, NULL, MS_BIND|MS_REC, NULL) < 0)
122 errExit("mount bind");
123
124 free(wfile);
125}
126
127
128// whitelist for /home/user directory
129void fs_whitelist(void) {
130 char *homedir = cfg.homedir;
131 assert(homedir);
132 ProfileEntry *entry = cfg.profile;
133 if (!entry)
134 return;
135
136 // realpath function will fail with ENOENT if the file is not found
137 // we need to expand the path before installing a new, empty home directory
138 while (entry) {
139 // handle only whitelist commands
140 if (strncmp(entry->data, "whitelist ", 10)) {
141 entry = entry->next;
142 continue;
143 }
144
145 char *new_name = expand_home(entry->data + 10, cfg.homedir);
146 assert(new_name);
147 char *fname = realpath(new_name, NULL);
148 free(new_name);
149 if (fname) {
150 // change file name in entry->data
151 if (strcmp(fname, entry->data + 10) != 0) {
152 char *newdata;
153 if (asprintf(&newdata, "whitelist %s", fname) == -1)
154 errExit("asprintf");
155 entry->data = newdata;
156 if (arg_debug)
157 printf("Replaced whitelist path: %s\n", entry->data);
158 }
159
160 free(fname);
161 }
162 else {
163 // file not found, blank the entry in the list
164 if (arg_debug)
165 printf("Removed whitelist path: %s\n", entry->data);
166 *entry->data = '\0';
167 }
168 entry = entry->next;
169 }
170
171 // create /tmp/firejail/mnt/whome directory
172 fs_build_mnt_dir();
173 int rv = mkdir(WHITELIST_HOME_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
174 if (rv == -1)
175 errExit("mkdir");
176 if (chown(WHITELIST_HOME_DIR, getuid(), getgid()) < 0)
177 errExit("chown");
178 if (chmod(WHITELIST_HOME_DIR, 0755) < 0)
179 errExit("chmod");
180
181 // keep a copy of real home dir in /tmp/firejail/mnt/whome
182 if (mount(cfg.homedir, WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
183 errExit("mount bind");
184
185 // start building the new home directory by mounting a tmpfs fielsystem
186 fs_private();
187
188 // go through profile rules again, and interpret whitelist commands
189 entry = cfg.profile;
190 while (entry) {
191 // handle only whitelist commands
192 if (strncmp(entry->data, "whitelist ", 10)) {
193 entry = entry->next;
194 continue;
195 }
196
197 whitelist_path(entry->data + 10);
198
199 entry = entry->next;
200 }
201
202 // mask the real home directory, currently mounted on /tmp/firejail/mnt/whome
203 if (mount("tmpfs", WHITELIST_HOME_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
204 errExit("mount tmpfs");
205}
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 43a468c46..60c2a7cec 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -82,6 +82,7 @@ int arg_shell_none = 0; // run the program directly without a shell
82int arg_private_dev = 0; // private dev directory 82int arg_private_dev = 0; // private dev directory
83int arg_private_etc = 0; // private etc directory 83int arg_private_etc = 0; // private etc directory
84int arg_scan = 0; // arp-scan all interfaces 84int arg_scan = 0; // arp-scan all interfaces
85int arg_whitelist = 0; // whitelist commad
85 86
86int parent_to_child_fds[2]; 87int parent_to_child_fds[2];
87int child_to_parent_fds[2]; 88int child_to_parent_fds[2];
@@ -581,6 +582,14 @@ int main(int argc, char **argv) {
581 profile_check_line(line, 0); // will exit if something wrong 582 profile_check_line(line, 0); // will exit if something wrong
582 profile_add(line); 583 profile_add(line);
583 } 584 }
585 else if (strncmp(argv[i], "--whitelist=", 12) == 0) {
586 char *line;
587 if (asprintf(&line, "whitelist %s", argv[i] + 12) == -1)
588 errExit("asprintf");
589
590 profile_check_line(line, 0); // will exit if something wrong
591 profile_add(line);
592 }
584 else if (strncmp(argv[i], "--read-only=", 12) == 0) { 593 else if (strncmp(argv[i], "--read-only=", 12) == 0) {
585 char *line; 594 char *line;
586 if (asprintf(&line, "read-only %s", argv[i] + 12) == -1) 595 if (asprintf(&line, "read-only %s", argv[i] + 12) == -1)
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index 778478321..2863b454e 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -336,6 +336,10 @@ int profile_check_line(char *ptr, int lineno) {
336 ptr += 10; 336 ptr += 10;
337 else if (strncmp(ptr, "noblacklist ", 12) == 0) 337 else if (strncmp(ptr, "noblacklist ", 12) == 0)
338 ptr += 12; 338 ptr += 12;
339 else if (strncmp(ptr, "whitelist ", 10) == 0) {
340 arg_whitelist = 1;
341 ptr += 10;
342 }
339 else if (strncmp(ptr, "read-only ", 10) == 0) 343 else if (strncmp(ptr, "read-only ", 10) == 0)
340 ptr += 10; 344 ptr += 10;
341 else if (strncmp(ptr, "tmpfs ", 6) == 0) 345 else if (strncmp(ptr, "tmpfs ", 6) == 0)
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 53782a288..41fc20084 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -232,8 +232,14 @@ int sandbox(void* sandbox_arg) {
232 //**************************** 232 //****************************
233 // apply the profile file 233 // apply the profile file
234 //**************************** 234 //****************************
235 if (cfg.profile) 235 if (cfg.profile) {
236 fs_blacklist(cfg.homedir); 236 // apply all whitelist commands ...
237 if (arg_whitelist)
238 fs_whitelist();
239
240 // ... followed by blacklist commands
241 fs_blacklist();
242 }
237 243
238 //**************************** 244 //****************************
239 // private mode 245 // private mode
diff --git a/src/firejail/usage.c b/src/firejail/usage.c
index fbb36fad7..1f611001d 100644
--- a/src/firejail/usage.c
+++ b/src/firejail/usage.c
@@ -227,6 +227,7 @@ void usage(void) {
227 printf("\t--trace - trace open, access and connect system calls.\n\n"); 227 printf("\t--trace - trace open, access and connect system calls.\n\n");
228 printf("\t--tree - print a tree of all sandboxed processes.\n\n"); 228 printf("\t--tree - print a tree of all sandboxed processes.\n\n");
229 printf("\t--version - print program version and exit.\n\n"); 229 printf("\t--version - print program version and exit.\n\n");
230 printf("\t--whitelist=dirname_or_filename - whitelist directory or file.\n\n");
230 printf("\t--zsh - use /usr/bin/zsh as default shell.\n\n"); 231 printf("\t--zsh - use /usr/bin/zsh as default shell.\n\n");
231 printf("\n"); 232 printf("\n");
232 printf("\n"); 233 printf("\n");
diff --git a/src/man/firejail-login.txt b/src/man/firejail-login.txt
index 1d6a8d80e..c3ee50ecf 100644
--- a/src/man/firejail-login.txt
+++ b/src/man/firejail-login.txt
@@ -16,9 +16,11 @@ Example:
16.SH RESTRICTED SHELL 16.SH RESTRICTED SHELL
17To configure a restricted shell, replace /bin/bash with /usr/bin/firejail in 17To configure a restricted shell, replace /bin/bash with /usr/bin/firejail in
18/etc/password file for each user that needs to be restricted. Alternatively, 18/etc/password file for each user that needs to be restricted. Alternatively,
19you can specify /usr/bin/firejail in adduser command: 19you can specify /usr/bin/firejail using adduser or usermod commands:
20 20
21adduser \-\-shell /usr/bin/firejail username 21adduser \-\-shell /usr/bin/firejail username
22.br
23usermod \-\-shell /usr/bin/firejail username
22 24
23.SH FILES 25.SH FILES
24/etc/firejail/login.users 26/etc/firejail/login.users
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index cfd00456b..4bf537c95 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -109,7 +109,7 @@ $ firejail \-\-blacklist=/sbin \-\-blacklist=/usr/sbin
109.br 109.br
110$ firejail \-\-blacklist=~/.mozilla 110$ firejail \-\-blacklist=~/.mozilla
111.br 111.br
112$ firejail "\-\-blacklist=My Virtual Machines" 112$ firejail "\-\-blacklist=/home/username/My Virtual Machines"
113.TP 113.TP
114\fB\-c 114\fB\-c
115Execute command and exit. 115Execute command and exit.
@@ -1091,6 +1091,17 @@ $ firejail \-\-version
1091.br 1091.br
1092firejail version 0.9.27 1092firejail version 0.9.27
1093.TP 1093.TP
1094\fB\-\-whitelist=dirname_or_filename
1095Whitelist directory or file. Only files in user home directory are accepted.
1096.br
1097
1098.br
1099Example:
1100.br
1101$ firejail \-\-whitelist=~/.mozilla \-\-whitelist=~/Downloads
1102.br
1103$ firejail "\-\-whitelist=/home/username/My Virtual Machines"
1104.TP
1094\fB\-\-zsh 1105\fB\-\-zsh
1095Use /usr/bin/zsh as default user shell. 1106Use /usr/bin/zsh as default user shell.
1096.br 1107.br