diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/firejail.h | 9 | ||||
-rw-r--r-- | src/firejail/fs_whitelist.c | 188 | ||||
-rw-r--r-- | src/firejail/profile.c | 1 |
3 files changed, 142 insertions, 56 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 58c497cd8..e3334bd2e 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -39,7 +39,8 @@ | |||
39 | #define DRI_DIR "/run/firejail/mnt/dri" | 39 | #define DRI_DIR "/run/firejail/mnt/dri" |
40 | #define PULSE_DIR "/run/firejail/mnt/pulse" | 40 | #define PULSE_DIR "/run/firejail/mnt/pulse" |
41 | #define DEVLOG_FILE "/run/firejail/mnt/devlog" | 41 | #define DEVLOG_FILE "/run/firejail/mnt/devlog" |
42 | #define WHITELIST_HOME_DIR "/run/firejail/mnt/whome" | 42 | #define WHITELIST_HOME_DIR "/run/firejail/mnt/orig-home" |
43 | #define WHITELIST_TMP_DIR "/run/firejail/mnt/orig-tmp" | ||
43 | #define XAUTHORITY_FILE "/run/firejail/mnt/.Xauthority" | 44 | #define XAUTHORITY_FILE "/run/firejail/mnt/.Xauthority" |
44 | #define HOSTNAME_FILE "/run/firejail/mnt/hostname" | 45 | #define HOSTNAME_FILE "/run/firejail/mnt/hostname" |
45 | #define RESOLVCONF_FILE "/run/firejail/mnt/resolv.conf" | 46 | #define RESOLVCONF_FILE "/run/firejail/mnt/resolv.conf" |
@@ -86,8 +87,12 @@ typedef struct interface_t { | |||
86 | 87 | ||
87 | typedef struct profile_entry_t { | 88 | typedef struct profile_entry_t { |
88 | struct profile_entry_t *next; | 89 | struct profile_entry_t *next; |
89 | char *data; // expanded name of the file | 90 | char *data; // command |
91 | |||
92 | // whitelist command parameters | ||
90 | char *link; // link name - set if the file is a link | 93 | char *link; // link name - set if the file is a link |
94 | unsigned home_dir:1; // whitelist in /home/user directory | ||
95 | unsigned tmp_dir:1; // whitelist in /tmp directory | ||
91 | }ProfileEntry; | 96 | }ProfileEntry; |
92 | 97 | ||
93 | typedef struct config_t { | 98 | typedef struct config_t { |
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index c856359f6..fd9115a5e 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -56,26 +56,40 @@ static int mkpath(const char* path, mode_t mode) { | |||
56 | return 0; | 56 | return 0; |
57 | } | 57 | } |
58 | 58 | ||
59 | static void whitelist_path(const char *path) { | 59 | static void whitelist_path(ProfileEntry *entry) { |
60 | assert(entry); | ||
61 | char *path = entry->data + 10; | ||
60 | assert(path); | 62 | assert(path); |
63 | const char *fname; | ||
64 | char *wfile; | ||
61 | 65 | ||
62 | // fname needs to start with /home/username | 66 | if (entry->home_dir) { |
63 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir))) { | 67 | printf("here %d\n", __LINE__); |
64 | fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path); | 68 | fname = path + strlen(cfg.homedir); |
65 | exit(1); | 69 | if (*fname == '\0') { |
66 | } | 70 | fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path); |
67 | 71 | exit(1); | |
68 | const char *fname = path + strlen(cfg.homedir); | 72 | } |
69 | if (*fname == '\0') { | 73 | |
70 | fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path); | 74 | if (asprintf(&wfile, "%s/%s", WHITELIST_HOME_DIR, fname) == -1) |
71 | exit(1); | 75 | errExit("asprintf"); |
72 | } | 76 | } |
77 | else if (entry->tmp_dir) { | ||
78 | printf("here %d\n", __LINE__); | ||
79 | fname = path + 4; // strlen("/tmp") | ||
80 | if (*fname == '\0') { | ||
81 | fprintf(stderr, "Error: file %s is not in /tmp directory, exiting...\n", path); | ||
82 | exit(1); | ||
83 | } | ||
73 | 84 | ||
74 | char *wfile; | 85 | if (asprintf(&wfile, "%s/%s", WHITELIST_TMP_DIR, fname) == -1) |
75 | if (asprintf(&wfile, "%s/%s", WHITELIST_HOME_DIR, fname) == -1) | 86 | errExit("asprintf"); |
76 | errExit("asprintf"); | 87 | } |
77 | 88 | ||
78 | // check if the file exists | 89 | // check if the file exists |
90 | printf("here %d %s\n", __LINE__, wfile); | ||
91 | system("ls -l /run/firejail/mnt/orig-tmp"); | ||
92 | |||
79 | struct stat s; | 93 | struct stat s; |
80 | if (stat(wfile, &s) == 0) { | 94 | if (stat(wfile, &s) == 0) { |
81 | if (arg_debug) | 95 | if (arg_debug) |
@@ -132,9 +146,12 @@ void fs_whitelist(void) { | |||
132 | ProfileEntry *entry = cfg.profile; | 146 | ProfileEntry *entry = cfg.profile; |
133 | if (!entry) | 147 | if (!entry) |
134 | return; | 148 | return; |
135 | 149 | ||
136 | // realpath function will fail with ENOENT if the file is not found | 150 | char *new_name = NULL; |
137 | // we need to expand the path before installing a new, empty home directory | 151 | int home_dir = 0; // /home/user directory flag |
152 | int tmp_dir = 0; // /tmp directory flag | ||
153 | |||
154 | // verify whitelist files, extract symbolic links, etc. | ||
138 | while (entry) { | 155 | while (entry) { |
139 | // handle only whitelist commands | 156 | // handle only whitelist commands |
140 | if (strncmp(entry->data, "whitelist ", 10)) { | 157 | if (strncmp(entry->data, "whitelist ", 10)) { |
@@ -142,10 +159,42 @@ void fs_whitelist(void) { | |||
142 | continue; | 159 | continue; |
143 | } | 160 | } |
144 | 161 | ||
145 | char *new_name = expand_home(entry->data + 10, cfg.homedir); | 162 | // replace ~/ or ${HOME} into /home/username |
146 | 163 | new_name = expand_home(entry->data + 10, cfg.homedir); | |
147 | assert(new_name); | 164 | assert(new_name); |
165 | |||
166 | // extract the absolute path of the file | ||
167 | // realpath function will fail with ENOENT if the file is not found | ||
148 | char *fname = realpath(new_name, NULL); | 168 | char *fname = realpath(new_name, NULL); |
169 | if (!fname) { | ||
170 | // file not found, blank the entry in the list and continue | ||
171 | if (arg_debug) | ||
172 | printf("Removed whitelist path: %s\n", entry->data); | ||
173 | *entry->data = '\0'; | ||
174 | continue; | ||
175 | } | ||
176 | |||
177 | // valid path referenced to filesystem root | ||
178 | if (*new_name != '/') | ||
179 | goto errexit; | ||
180 | |||
181 | // check for home directory or tmp directory | ||
182 | if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { | ||
183 | entry->home_dir = 1; | ||
184 | home_dir = 1; | ||
185 | // both path and absolute path are under /home | ||
186 | if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) != 0) | ||
187 | goto errexit; | ||
188 | } | ||
189 | else if (strncmp(new_name, "/tmp/", 5) == 0) { | ||
190 | entry->tmp_dir = 1; | ||
191 | tmp_dir = 1; | ||
192 | // both path and absolute path are under /tmp | ||
193 | if (strncmp(fname, "/tmp/", 5) != 0) | ||
194 | goto errexit; | ||
195 | } | ||
196 | else | ||
197 | goto errexit; | ||
149 | 198 | ||
150 | // mark symbolic links | 199 | // mark symbolic links |
151 | if (is_link(new_name)) | 200 | if (is_link(new_name)) |
@@ -153,44 +202,60 @@ void fs_whitelist(void) { | |||
153 | else | 202 | else |
154 | free(new_name); | 203 | free(new_name); |
155 | 204 | ||
156 | if (fname) { | 205 | // change file name in entry->data |
157 | // change file name in entry->data | 206 | if (strcmp(fname, entry->data + 10) != 0) { |
158 | if (strcmp(fname, entry->data + 10) != 0) { | 207 | char *newdata; |
159 | char *newdata; | 208 | if (asprintf(&newdata, "whitelist %s", fname) == -1) |
160 | if (asprintf(&newdata, "whitelist %s", fname) == -1) | 209 | errExit("asprintf"); |
161 | errExit("asprintf"); | 210 | entry->data = newdata; |
162 | entry->data = newdata; | ||
163 | if (arg_debug) | ||
164 | printf("Replaced whitelist path: %s\n", entry->data); | ||
165 | } | ||
166 | |||
167 | free(fname); | ||
168 | } | ||
169 | else { | ||
170 | // file not found, blank the entry in the list | ||
171 | if (arg_debug) | 211 | if (arg_debug) |
172 | printf("Removed whitelist path: %s\n", entry->data); | 212 | printf("Replaced whitelist path: %s\n", entry->data); |
173 | *entry->data = '\0'; | ||
174 | } | 213 | } |
214 | free(fname); | ||
175 | entry = entry->next; | 215 | entry = entry->next; |
176 | } | 216 | } |
177 | 217 | ||
178 | // create /tmp/firejail/mnt/whome directory | 218 | // create mount points |
179 | fs_build_mnt_dir(); | 219 | fs_build_mnt_dir(); |
180 | int rv = mkdir(WHITELIST_HOME_DIR, S_IRWXU | S_IRWXG | S_IRWXO); | ||
181 | if (rv == -1) | ||
182 | errExit("mkdir"); | ||
183 | if (chown(WHITELIST_HOME_DIR, getuid(), getgid()) < 0) | ||
184 | errExit("chown"); | ||
185 | if (chmod(WHITELIST_HOME_DIR, 0755) < 0) | ||
186 | errExit("chmod"); | ||
187 | 220 | ||
188 | // keep a copy of real home dir in /tmp/firejail/mnt/whome | 221 | // /home/user |
189 | if (mount(cfg.homedir, WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 222 | if (home_dir) { |
190 | errExit("mount bind"); | 223 | // keep a copy of real home dir in WHITELIST_HOME_DIR |
191 | 224 | int rv = mkdir(WHITELIST_HOME_DIR, S_IRWXU | S_IRWXG | S_IRWXO); | |
192 | // start building the new home directory by mounting a tmpfs fielsystem | 225 | if (rv == -1) |
193 | fs_private(); | 226 | errExit("mkdir"); |
227 | if (chown(WHITELIST_HOME_DIR, getuid(), getgid()) < 0) | ||
228 | errExit("chown"); | ||
229 | if (chmod(WHITELIST_HOME_DIR, 0755) < 0) | ||
230 | errExit("chmod"); | ||
231 | |||
232 | if (mount(cfg.homedir, WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
233 | errExit("mount bind"); | ||
234 | |||
235 | // mount a tmpfs and initialize /home/user | ||
236 | fs_private(); | ||
237 | } | ||
238 | |||
239 | // /tmp mountpoint | ||
240 | if (tmp_dir) { | ||
241 | // keep a copy of real /tmp directory in WHITELIST_TMP_DIR | ||
242 | int rv = mkdir(WHITELIST_TMP_DIR, S_IRWXU | S_IRWXG | S_IRWXO); | ||
243 | if (rv == -1) | ||
244 | errExit("mkdir"); | ||
245 | if (chown(WHITELIST_TMP_DIR, 0, 0) < 0) | ||
246 | errExit("chown"); | ||
247 | if (chmod(WHITELIST_TMP_DIR, 0777) < 0) | ||
248 | errExit("chmod"); | ||
249 | |||
250 | if (mount("/tmp", WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
251 | errExit("mount bind"); | ||
252 | |||
253 | // mount tmpfs on /tmp | ||
254 | if (arg_debug) | ||
255 | printf("Mounting tmpfs on /tmp directory\n"); | ||
256 | if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) | ||
257 | errExit("mounting tmpfs on /tmpt"); | ||
258 | } | ||
194 | 259 | ||
195 | // go through profile rules again, and interpret whitelist commands | 260 | // go through profile rules again, and interpret whitelist commands |
196 | entry = cfg.profile; | 261 | entry = cfg.profile; |
@@ -201,8 +266,9 @@ void fs_whitelist(void) { | |||
201 | continue; | 266 | continue; |
202 | } | 267 | } |
203 | 268 | ||
269 | //printf("here %d#%s#\n", __LINE__, entry->data); | ||
204 | // whitelist the real file | 270 | // whitelist the real file |
205 | whitelist_path(entry->data + 10); | 271 | whitelist_path(entry); |
206 | 272 | ||
207 | // create the link if any | 273 | // create the link if any |
208 | if (entry->link) { | 274 | if (entry->link) { |
@@ -220,7 +286,21 @@ void fs_whitelist(void) { | |||
220 | entry = entry->next; | 286 | entry = entry->next; |
221 | } | 287 | } |
222 | 288 | ||
223 | // mask the real home directory, currently mounted on /tmp/firejail/mnt/whome | 289 | // mask the real home directory, currently mounted on WHITELIST_HOME_DIR |
224 | if (mount("tmpfs", WHITELIST_HOME_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 290 | if (home_dir) { |
225 | errExit("mount tmpfs"); | 291 | if (mount("tmpfs", WHITELIST_HOME_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
292 | errExit("mount tmpfs"); | ||
293 | } | ||
294 | |||
295 | // mask the real /tmp directory, currently mounted on WHITELIST_TMP_DIR | ||
296 | if (tmp_dir) { | ||
297 | if (mount("tmpfs", WHITELIST_TMP_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
298 | errExit("mount tmpfs"); | ||
299 | } | ||
300 | |||
301 | return; | ||
302 | |||
303 | errexit: | ||
304 | fprintf(stderr, "Error: invalid whitelist path %s\n", new_name); | ||
305 | exit(1); | ||
226 | } | 306 | } |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index aeeacfde8..73407d9c0 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -398,6 +398,7 @@ void profile_add(char *str) { | |||
398 | ProfileEntry *prf = malloc(sizeof(ProfileEntry)); | 398 | ProfileEntry *prf = malloc(sizeof(ProfileEntry)); |
399 | if (!prf) | 399 | if (!prf) |
400 | errExit("malloc"); | 400 | errExit("malloc"); |
401 | memset(prf, 0, sizeof(ProfileEntry)); | ||
401 | prf->next = NULL; | 402 | prf->next = NULL; |
402 | prf->data = str; | 403 | prf->data = str; |
403 | 404 | ||