diff options
-rw-r--r-- | src/firejail/firejail.h | 4 | ||||
-rw-r--r-- | src/firejail/fs.c | 13 | ||||
-rw-r--r-- | src/firejail/fs_etc.c | 126 | ||||
-rw-r--r-- | src/firejail/fs_hostname.c | 103 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 2 |
5 files changed, 143 insertions, 105 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 6c9d70c0b..545573c08 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -156,6 +156,8 @@ typedef struct config_t { | |||
156 | 156 | ||
157 | // filesystem | 157 | // filesystem |
158 | ProfileEntry *profile; | 158 | ProfileEntry *profile; |
159 | ProfileEntry *profile_rebuild_etc; // blacklist files in /etc directory used by fs_rebuild_etc() | ||
160 | |||
159 | #define MAX_PROFILE_IGNORE 32 | 161 | #define MAX_PROFILE_IGNORE 32 |
160 | char *profile_ignore[MAX_PROFILE_IGNORE]; | 162 | char *profile_ignore[MAX_PROFILE_IGNORE]; |
161 | char *chrootdir; // chroot directory | 163 | char *chrootdir; // chroot directory |
@@ -625,7 +627,6 @@ void fs_trace(void); | |||
625 | 627 | ||
626 | // fs_hostname.c | 628 | // fs_hostname.c |
627 | void fs_hostname(const char *hostname); | 629 | void fs_hostname(const char *hostname); |
628 | void fs_resolvconf(void); | ||
629 | char *fs_check_hosts_file(const char *fname); | 630 | char *fs_check_hosts_file(const char *fname); |
630 | void fs_store_hosts_file(void); | 631 | void fs_store_hosts_file(void); |
631 | void fs_mount_hosts_file(void); | 632 | void fs_mount_hosts_file(void); |
@@ -668,6 +669,7 @@ void fs_machineid(void); | |||
668 | void fs_private_dir_copy(const char *private_dir, const char *private_run_dir, const char *private_list); | 669 | void fs_private_dir_copy(const char *private_dir, const char *private_run_dir, const char *private_list); |
669 | void fs_private_dir_mount(const char *private_dir, const char *private_run_dir); | 670 | void fs_private_dir_mount(const char *private_dir, const char *private_run_dir); |
670 | void fs_private_dir_list(const char *private_dir, const char *private_run_dir, const char *private_list); | 671 | void fs_private_dir_list(const char *private_dir, const char *private_run_dir, const char *private_list); |
672 | void fs_rebuild_etc(void); | ||
671 | 673 | ||
672 | // no_sandbox.c | 674 | // no_sandbox.c |
673 | int check_namespace_virt(void); | 675 | int check_namespace_virt(void); |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 0e26eb505..dd170ad19 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -162,6 +162,19 @@ static void disable_file(OPERATION op, const char *filename) { | |||
162 | fs_logger2("blacklist", fname); | 162 | fs_logger2("blacklist", fname); |
163 | else | 163 | else |
164 | fs_logger2("blacklist-nolog", fname); | 164 | fs_logger2("blacklist-nolog", fname); |
165 | |||
166 | // files in /etc will be reprocessed during /etc rebuild | ||
167 | if (strncmp(fname, "/etc/", 5) == 0) { | ||
168 | ProfileEntry *prf = malloc(sizeof(ProfileEntry)); | ||
169 | if (!prf) | ||
170 | errExit("malloc"); | ||
171 | memset(prf, 0, sizeof(ProfileEntry)); | ||
172 | prf->data = strdup(fname); | ||
173 | if (!prf->data) | ||
174 | errExit("strdup"); | ||
175 | prf->next = cfg.profile_rebuild_etc; | ||
176 | cfg.profile_rebuild_etc = prf; | ||
177 | } | ||
165 | } | 178 | } |
166 | } | 179 | } |
167 | else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { | 180 | else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { |
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c index b0e1e1bf1..76054b485 100644 --- a/src/firejail/fs_etc.c +++ b/src/firejail/fs_etc.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <sys/types.h> | 24 | #include <sys/types.h> |
25 | #include <time.h> | 25 | #include <time.h> |
26 | #include <unistd.h> | 26 | #include <unistd.h> |
27 | #include <dirent.h> | ||
27 | 28 | ||
28 | // spoof /etc/machine_id | 29 | // spoof /etc/machine_id |
29 | void fs_machineid(void) { | 30 | void fs_machineid(void) { |
@@ -250,3 +251,128 @@ void fs_private_dir_list(const char *private_dir, const char *private_run_dir, c | |||
250 | fs_private_dir_mount(private_dir, private_run_dir); | 251 | fs_private_dir_mount(private_dir, private_run_dir); |
251 | fmessage("Private %s installed in %0.2f ms\n", private_dir, timetrace_end()); | 252 | fmessage("Private %s installed in %0.2f ms\n", private_dir, timetrace_end()); |
252 | } | 253 | } |
254 | |||
255 | void fs_rebuild_etc(void) { | ||
256 | int have_dhcp = 1; | ||
257 | if (cfg.dns1 == NULL && !any_dhcp()) | ||
258 | have_dhcp = 0; | ||
259 | |||
260 | if (arg_debug) | ||
261 | printf("rebuilding /etc directory\n"); | ||
262 | if (mkdir(RUN_DNS_ETC, 0755)) | ||
263 | errExit("mkdir"); | ||
264 | selinux_relabel_path(RUN_DNS_ETC, "/etc"); | ||
265 | fs_logger("tmpfs /etc"); | ||
266 | |||
267 | DIR *dir = opendir("/etc"); | ||
268 | if (!dir) | ||
269 | errExit("opendir"); | ||
270 | |||
271 | struct stat s; | ||
272 | struct dirent *entry; | ||
273 | while ((entry = readdir(dir))) { | ||
274 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) | ||
275 | continue; | ||
276 | |||
277 | // skip files in cfg.profile_rebuild_etc list | ||
278 | // these files are already blacklisted | ||
279 | { | ||
280 | ProfileEntry *prf = cfg.profile_rebuild_etc; | ||
281 | int found = 0; | ||
282 | while (prf) { | ||
283 | if (strcmp(entry->d_name, prf->data + 5) == 0) { // 5 is strlen("/etc/") | ||
284 | found = 1; | ||
285 | break; | ||
286 | } | ||
287 | prf = prf->next; | ||
288 | } | ||
289 | if (found) | ||
290 | continue; | ||
291 | } | ||
292 | |||
293 | // for resolv.conf we might have to create a brand new file later | ||
294 | if (have_dhcp && | ||
295 | (strcmp(entry->d_name, "resolv.conf") == 0 || | ||
296 | strcmp(entry->d_name, "resolv.conf.dhclient-new") == 0)) | ||
297 | continue; | ||
298 | // printf("linking %s\n", entry->d_name); | ||
299 | |||
300 | char *src; | ||
301 | if (asprintf(&src, "/etc/%s", entry->d_name) == -1) | ||
302 | errExit("asprintf"); | ||
303 | if (stat(src, &s) != 0) { | ||
304 | free(src); | ||
305 | continue; | ||
306 | } | ||
307 | |||
308 | char *dest; | ||
309 | if (asprintf(&dest, "%s/%s", RUN_DNS_ETC, entry->d_name) == -1) | ||
310 | errExit("asprintf"); | ||
311 | |||
312 | int symlink_done = 0; | ||
313 | if (is_link(src)) { | ||
314 | char *rp =realpath(src, NULL); | ||
315 | if (rp == NULL) { | ||
316 | free(src); | ||
317 | free(dest); | ||
318 | continue; | ||
319 | } | ||
320 | if (symlink(rp, dest)) | ||
321 | errExit("symlink"); | ||
322 | else | ||
323 | symlink_done = 1; | ||
324 | } | ||
325 | else if (S_ISDIR(s.st_mode)) | ||
326 | create_empty_dir_as_root(dest, s.st_mode); | ||
327 | else | ||
328 | create_empty_file_as_root(dest, s.st_mode); | ||
329 | |||
330 | // bind-mount src on top of dest | ||
331 | if (!symlink_done) { | ||
332 | if (mount(src, dest, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
333 | errExit("mount bind mirroring /etc"); | ||
334 | } | ||
335 | fs_logger2("clone", src); | ||
336 | |||
337 | free(src); | ||
338 | free(dest); | ||
339 | } | ||
340 | closedir(dir); | ||
341 | |||
342 | // mount bind our private etc directory on top of /etc | ||
343 | if (arg_debug) | ||
344 | printf("Mount-bind %s on top of /etc\n", RUN_DNS_ETC); | ||
345 | if (mount(RUN_DNS_ETC, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) | ||
346 | errExit("mount bind mirroring /etc"); | ||
347 | fs_logger("mount /etc"); | ||
348 | |||
349 | if (have_dhcp == 0) | ||
350 | return; | ||
351 | |||
352 | if (arg_debug) | ||
353 | printf("Creating a new /etc/resolv.conf file\n"); | ||
354 | FILE *fp = fopen("/etc/resolv.conf", "wxe"); | ||
355 | if (!fp) { | ||
356 | fprintf(stderr, "Error: cannot create /etc/resolv.conf file\n"); | ||
357 | exit(1); | ||
358 | } | ||
359 | |||
360 | if (cfg.dns1) { | ||
361 | if (any_dhcp()) | ||
362 | fwarning("network setup uses DHCP, nameservers will likely be overwritten\n"); | ||
363 | fprintf(fp, "nameserver %s\n", cfg.dns1); | ||
364 | } | ||
365 | if (cfg.dns2) | ||
366 | fprintf(fp, "nameserver %s\n", cfg.dns2); | ||
367 | if (cfg.dns3) | ||
368 | fprintf(fp, "nameserver %s\n", cfg.dns3); | ||
369 | if (cfg.dns4) | ||
370 | fprintf(fp, "nameserver %s\n", cfg.dns4); | ||
371 | |||
372 | // mode and owner | ||
373 | SET_PERMS_STREAM(fp, 0, 0, 0644); | ||
374 | |||
375 | fclose(fp); | ||
376 | |||
377 | fs_logger("create /etc/resolv.conf"); | ||
378 | } | ||
diff --git a/src/firejail/fs_hostname.c b/src/firejail/fs_hostname.c index 80046f7ae..1a9a78ceb 100644 --- a/src/firejail/fs_hostname.c +++ b/src/firejail/fs_hostname.c | |||
@@ -88,109 +88,6 @@ errexit: | |||
88 | exit(1); | 88 | exit(1); |
89 | } | 89 | } |
90 | 90 | ||
91 | void fs_resolvconf(void) { | ||
92 | if (cfg.dns1 == NULL && !any_dhcp()) | ||
93 | return; | ||
94 | |||
95 | if (arg_debug) | ||
96 | printf("mirroring /etc directory\n"); | ||
97 | if (mkdir(RUN_DNS_ETC, 0755)) | ||
98 | errExit("mkdir"); | ||
99 | selinux_relabel_path(RUN_DNS_ETC, "/etc"); | ||
100 | fs_logger("tmpfs /etc"); | ||
101 | |||
102 | DIR *dir = opendir("/etc"); | ||
103 | if (!dir) | ||
104 | errExit("opendir"); | ||
105 | |||
106 | struct stat s; | ||
107 | struct dirent *entry; | ||
108 | while ((entry = readdir(dir))) { | ||
109 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) | ||
110 | continue; | ||
111 | // for resolv.conf we create a brand new file | ||
112 | if (strcmp(entry->d_name, "resolv.conf") == 0 || | ||
113 | strcmp(entry->d_name, "resolv.conf.dhclient-new") == 0) | ||
114 | continue; | ||
115 | // printf("linking %s\n", entry->d_name); | ||
116 | |||
117 | char *src; | ||
118 | if (asprintf(&src, "/etc/%s", entry->d_name) == -1) | ||
119 | errExit("asprintf"); | ||
120 | if (stat(src, &s) != 0) { | ||
121 | free(src); | ||
122 | continue; | ||
123 | } | ||
124 | |||
125 | char *dest; | ||
126 | if (asprintf(&dest, "%s/%s", RUN_DNS_ETC, entry->d_name) == -1) | ||
127 | errExit("asprintf"); | ||
128 | |||
129 | int symlink_done = 0; | ||
130 | if (is_link(src)) { | ||
131 | char *rp =realpath(src, NULL); | ||
132 | if (rp == NULL) { | ||
133 | free(src); | ||
134 | free(dest); | ||
135 | continue; | ||
136 | } | ||
137 | if (symlink(rp, dest)) | ||
138 | errExit("symlink"); | ||
139 | else | ||
140 | symlink_done = 1; | ||
141 | } | ||
142 | else if (S_ISDIR(s.st_mode)) | ||
143 | create_empty_dir_as_root(dest, s.st_mode); | ||
144 | else | ||
145 | create_empty_file_as_root(dest, s.st_mode); | ||
146 | |||
147 | // bind-mount src on top of dest | ||
148 | if (!symlink_done) { | ||
149 | if (mount(src, dest, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
150 | errExit("mount bind mirroring /etc"); | ||
151 | } | ||
152 | fs_logger2("clone", src); | ||
153 | |||
154 | free(src); | ||
155 | free(dest); | ||
156 | } | ||
157 | closedir(dir); | ||
158 | |||
159 | // mount bind our private etc directory on top of /etc | ||
160 | if (arg_debug) | ||
161 | printf("Mount-bind %s on top of /etc\n", RUN_DNS_ETC); | ||
162 | if (mount(RUN_DNS_ETC, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) | ||
163 | errExit("mount bind mirroring /etc"); | ||
164 | fs_logger("mount /etc"); | ||
165 | |||
166 | if (arg_debug) | ||
167 | printf("Creating a new /etc/resolv.conf file\n"); | ||
168 | FILE *fp = fopen("/etc/resolv.conf", "wxe"); | ||
169 | if (!fp) { | ||
170 | fprintf(stderr, "Error: cannot create /etc/resolv.conf file\n"); | ||
171 | exit(1); | ||
172 | } | ||
173 | |||
174 | if (cfg.dns1) { | ||
175 | if (any_dhcp()) | ||
176 | fwarning("network setup uses DHCP, nameservers will likely be overwritten\n"); | ||
177 | fprintf(fp, "nameserver %s\n", cfg.dns1); | ||
178 | } | ||
179 | if (cfg.dns2) | ||
180 | fprintf(fp, "nameserver %s\n", cfg.dns2); | ||
181 | if (cfg.dns3) | ||
182 | fprintf(fp, "nameserver %s\n", cfg.dns3); | ||
183 | if (cfg.dns4) | ||
184 | fprintf(fp, "nameserver %s\n", cfg.dns4); | ||
185 | |||
186 | // mode and owner | ||
187 | SET_PERMS_STREAM(fp, 0, 0, 0644); | ||
188 | |||
189 | fclose(fp); | ||
190 | |||
191 | fs_logger("create /etc/resolv.conf"); | ||
192 | } | ||
193 | |||
194 | char *fs_check_hosts_file(const char *fname) { | 91 | char *fs_check_hosts_file(const char *fname) { |
195 | assert(fname); | 92 | assert(fname); |
196 | invalid_filename(fname, 0); // no globbing | 93 | invalid_filename(fname, 0); // no globbing |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index fd359c39e..59ddfb855 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -1043,7 +1043,7 @@ int sandbox(void* sandbox_arg) { | |||
1043 | //**************************** | 1043 | //**************************** |
1044 | // set dns | 1044 | // set dns |
1045 | //**************************** | 1045 | //**************************** |
1046 | fs_resolvconf(); | 1046 | fs_rebuild_etc(); |
1047 | 1047 | ||
1048 | //**************************** | 1048 | //**************************** |
1049 | // start dhcp client | 1049 | // start dhcp client |