aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/fs_whitelist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/fs_whitelist.c')
-rw-r--r--src/firejail/fs_whitelist.c205
1 files changed, 205 insertions, 0 deletions
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}