diff options
author | netblue30 <netblue30@yahoo.com> | 2015-08-12 08:36:41 -0400 |
---|---|---|
committer | netblue30 <netblue30@yahoo.com> | 2015-08-12 08:36:41 -0400 |
commit | 46334f0039100403572d7a6144d3a082cb648a42 (patch) | |
tree | c7e9d600b3ce213158ea9f26b9a87e9610269afb | |
parent | tenative fix for issue 11 (diff) | |
download | firejail-46334f0039100403572d7a6144d3a082cb648a42.tar.gz firejail-46334f0039100403572d7a6144d3a082cb648a42.tar.zst firejail-46334f0039100403572d7a6144d3a082cb648a42.zip |
--private-etc option, issue #5
-rw-r--r-- | src/firejail/firejail.h | 6 | ||||
-rw-r--r-- | src/firejail/fs_etc.c | 146 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 2 | ||||
-rw-r--r-- | src/firejail/main.c | 7 | ||||
-rw-r--r-- | src/firejail/profile.c | 10 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 2 | ||||
-rw-r--r-- | src/firejail/usage.c | 5 | ||||
-rw-r--r-- | src/man/firejail-profile.txt | 5 | ||||
-rw-r--r-- | src/man/firejail.txt | 13 |
9 files changed, 194 insertions, 2 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 2ec6e54c9..5adabbcb3 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 OVERLAY_DIR "/tmp/firejail/overlay" | 29 | #define OVERLAY_DIR "/tmp/firejail/overlay" |
30 | #define HOME_DIR "/tmp/firejail/mnt/home" | 30 | #define HOME_DIR "/tmp/firejail/mnt/home" |
31 | #define ETC_DIR "/tmp/firejail/mnt/etc" | ||
31 | #define MAX_INCLUDE_LEVEL 6 | 32 | #define MAX_INCLUDE_LEVEL 6 |
32 | 33 | ||
33 | // main.c | 34 | // main.c |
@@ -67,6 +68,7 @@ typedef struct config_t { | |||
67 | char *chrootdir; // chroot directory | 68 | char *chrootdir; // chroot directory |
68 | char *home_private; // private home directory | 69 | char *home_private; // private home directory |
69 | char *home_private_keep; // keep list for private home directory | 70 | char *home_private_keep; // keep list for private home directory |
71 | char *etc_private_keep; // keep list for private etc directory | ||
70 | char *cwd; // current working directory | 72 | char *cwd; // current working directory |
71 | 73 | ||
72 | // networking | 74 | // networking |
@@ -140,6 +142,7 @@ extern char *arg_netfilter_file; // netfilter file | |||
140 | extern int arg_doubledash; // double dash | 142 | extern int arg_doubledash; // double dash |
141 | extern int arg_shell_none; // run the program directly without a shell | 143 | extern int arg_shell_none; // run the program directly without a shell |
142 | extern int arg_private_dev; // private dev directory | 144 | extern int arg_private_dev; // private dev directory |
145 | extern int arg_private_etc; // private etc directory | ||
143 | extern int arg_scan; // arp-scan all interfaces | 146 | extern int arg_scan; // arp-scan all interfaces |
144 | 147 | ||
145 | extern int parent_to_child_fds[2]; | 148 | extern int parent_to_child_fds[2]; |
@@ -350,5 +353,8 @@ void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, in | |||
350 | void network_shm_del_file(pid_t pid); | 353 | void network_shm_del_file(pid_t pid); |
351 | void network_shm_set_file(pid_t pid); | 354 | void network_shm_set_file(pid_t pid); |
352 | 355 | ||
356 | // fs_etc.c | ||
357 | void fs_check_etc_list(void); | ||
358 | void fs_private_etc_list(void); | ||
353 | 359 | ||
354 | #endif \ No newline at end of file | 360 | #endif \ No newline at end of file |
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c new file mode 100644 index 000000000..57b0b4f3e --- /dev/null +++ b/src/firejail/fs_etc.c | |||
@@ -0,0 +1,146 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) | ||
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 <sys/types.h> | ||
24 | #include <sys/wait.h> | ||
25 | #include <unistd.h> | ||
26 | |||
27 | static void check_dir_or_file(const char *name) { | ||
28 | assert(name); | ||
29 | struct stat s; | ||
30 | char *fname; | ||
31 | if (asprintf(&fname, "/etc/%s", name) == -1) | ||
32 | errExit("asprintf"); | ||
33 | if (arg_debug) | ||
34 | printf("Checking %s\n", fname); | ||
35 | if (stat(fname, &s) == -1) { | ||
36 | fprintf(stderr, "Error: file %s not found.\n", fname); | ||
37 | exit(1); | ||
38 | } | ||
39 | |||
40 | // dir or regular file | ||
41 | if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { | ||
42 | free(fname); | ||
43 | return; | ||
44 | } | ||
45 | |||
46 | if (!is_link(fname)) { | ||
47 | free(fname); | ||
48 | return; | ||
49 | } | ||
50 | |||
51 | fprintf(stderr, "Error: invalid file type, %s.\n", fname); | ||
52 | exit(1); | ||
53 | } | ||
54 | |||
55 | void fs_check_etc_list(void) { | ||
56 | if (strstr(cfg.etc_private_keep, "..")) { | ||
57 | fprintf(stderr, "Error: invalid private etc list\n"); | ||
58 | exit(1); | ||
59 | } | ||
60 | |||
61 | char *dlist = strdup(cfg.etc_private_keep); | ||
62 | if (!dlist) | ||
63 | errExit("strdup"); | ||
64 | |||
65 | char *ptr = strtok(dlist, ","); | ||
66 | check_dir_or_file(ptr); | ||
67 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
68 | check_dir_or_file(ptr); | ||
69 | |||
70 | free(dlist); | ||
71 | } | ||
72 | |||
73 | static void duplicate(char *fname) { | ||
74 | char *cmd; | ||
75 | |||
76 | // copy the file | ||
77 | if (asprintf(&cmd, "cp -a --parents /etc/%s %s", fname, MNT_DIR) == -1) | ||
78 | errExit("asprintf"); | ||
79 | if (arg_debug) | ||
80 | printf("%s\n", cmd); | ||
81 | if (system(cmd)) | ||
82 | errExit("system cp -a --parents"); | ||
83 | free(cmd); | ||
84 | } | ||
85 | |||
86 | |||
87 | void fs_private_etc_list(void) { | ||
88 | char *private_list = cfg.etc_private_keep; | ||
89 | assert(private_list); | ||
90 | |||
91 | uid_t u = getuid(); | ||
92 | gid_t g = getgid(); | ||
93 | struct stat s; | ||
94 | if (stat("/etc", &s) == -1) { | ||
95 | fprintf(stderr, "Error: cannot find user /etc directory\n"); | ||
96 | exit(1); | ||
97 | } | ||
98 | |||
99 | // create /tmp/firejail/mnt/etc directory | ||
100 | fs_build_mnt_dir(); | ||
101 | int rv = mkdir(ETC_DIR, S_IRWXU | S_IRWXG | S_IRWXO); | ||
102 | if (rv == -1) | ||
103 | errExit("mkdir"); | ||
104 | if (chown(ETC_DIR, 0, 0) < 0) | ||
105 | errExit("chown"); | ||
106 | if (chmod(ETC_DIR, 0755) < 0) | ||
107 | errExit("chmod"); | ||
108 | |||
109 | // copy the list of files in the new etc directory | ||
110 | // using a new child process without root privileges | ||
111 | pid_t child = fork(); | ||
112 | if (child < 0) | ||
113 | errExit("fork"); | ||
114 | if (child == 0) { | ||
115 | if (arg_debug) | ||
116 | printf("Copying files in the new home:\n"); | ||
117 | |||
118 | // elevate privileges - files in the new /etc directory belong to root | ||
119 | if (setreuid(0, 0) < 0) | ||
120 | errExit("setreuid"); | ||
121 | if (setregid(0, 0) < 0) | ||
122 | errExit("setregid"); | ||
123 | |||
124 | // copy the list of files in the new home directory | ||
125 | char *dlist = strdup(private_list); | ||
126 | if (!dlist) | ||
127 | errExit("strdup"); | ||
128 | |||
129 | char *ptr = strtok(dlist, ","); | ||
130 | duplicate(ptr); | ||
131 | |||
132 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
133 | duplicate(ptr); | ||
134 | free(dlist); | ||
135 | exit(0); | ||
136 | } | ||
137 | // wait for the child to finish | ||
138 | waitpid(child, NULL, 0); | ||
139 | |||
140 | if (arg_debug) | ||
141 | printf("Mount-bind %s on top of /etc\n", ETC_DIR); | ||
142 | if (mount(ETC_DIR, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) | ||
143 | errExit("mount bind"); | ||
144 | |||
145 | } | ||
146 | |||
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 853e3930b..ca4691751 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -273,7 +273,7 @@ static void check_dir_or_file(const char *name) { | |||
273 | if (asprintf(&fname, "%s/%s", cfg.homedir, name) == -1) | 273 | if (asprintf(&fname, "%s/%s", cfg.homedir, name) == -1) |
274 | errExit("asprintf"); | 274 | errExit("asprintf"); |
275 | if (arg_debug) | 275 | if (arg_debug) |
276 | printf("***************Checking %s\n", fname); | 276 | printf("Checking %s\n", fname); |
277 | if (stat(fname, &s) == -1) { | 277 | if (stat(fname, &s) == -1) { |
278 | fprintf(stderr, "Error: file %s not found.\n", fname); | 278 | fprintf(stderr, "Error: file %s not found.\n", fname); |
279 | exit(1); | 279 | exit(1); |
diff --git a/src/firejail/main.c b/src/firejail/main.c index 78971aa86..1f4574c5c 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -81,6 +81,7 @@ char *arg_netfilter_file = NULL; // netfilter file | |||
81 | int arg_doubledash = 0; // double dash | 81 | int arg_doubledash = 0; // double dash |
82 | int arg_shell_none = 0; // run the program directly without a shell | 82 | int arg_shell_none = 0; // run the program directly without a shell |
83 | int arg_private_dev = 0; // private dev directory | 83 | int arg_private_dev = 0; // private dev directory |
84 | int arg_private_etc = 0; // private etc directory | ||
84 | int arg_scan = 0; // arp-scan all interfaces | 85 | int arg_scan = 0; // arp-scan all interfaces |
85 | 86 | ||
86 | int parent_to_child_fds[2]; | 87 | int parent_to_child_fds[2]; |
@@ -699,6 +700,12 @@ int main(int argc, char **argv) { | |||
699 | else if (strcmp(argv[i], "--private-dev") == 0) { | 700 | else if (strcmp(argv[i], "--private-dev") == 0) { |
700 | arg_private_dev = 1; | 701 | arg_private_dev = 1; |
701 | } | 702 | } |
703 | else if (strncmp(argv[i], "--private-etc=", 14) == 0) { | ||
704 | // extract private etc dirname | ||
705 | cfg.etc_private_keep = argv[i] + 14; | ||
706 | fs_check_etc_list(); | ||
707 | arg_private_etc = 1; | ||
708 | } | ||
702 | 709 | ||
703 | 710 | ||
704 | 711 | ||
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 877428637..a6843cc6d 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -227,13 +227,21 @@ int profile_check_line(char *ptr, int lineno) { | |||
227 | return 0; | 227 | return 0; |
228 | } | 228 | } |
229 | 229 | ||
230 | // private list of files and directories | 230 | // private home list of files and directories |
231 | if (strncmp(ptr, "private.keep ", 13) == 0) { | 231 | if (strncmp(ptr, "private.keep ", 13) == 0) { |
232 | cfg.home_private_keep = ptr + 13; | 232 | cfg.home_private_keep = ptr + 13; |
233 | fs_check_home_list(); | 233 | fs_check_home_list(); |
234 | arg_private = 1; | 234 | arg_private = 1; |
235 | return 0; | 235 | return 0; |
236 | } | 236 | } |
237 | |||
238 | // private /etc list of files and directories | ||
239 | if (strncmp(ptr, "private-etc ", 12) == 0) { | ||
240 | cfg.etc_private_keep = ptr + 12; | ||
241 | fs_check_etc_list(); | ||
242 | arg_private_etc = 1; | ||
243 | return 0; | ||
244 | } | ||
237 | 245 | ||
238 | // filesystem bind | 246 | // filesystem bind |
239 | if (strncmp(ptr, "bind ", 5) == 0) { | 247 | if (strncmp(ptr, "bind ", 5) == 0) { |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 3f5fb51fa..2beb31099 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -245,6 +245,8 @@ int sandbox(void* sandbox_arg) { | |||
245 | 245 | ||
246 | if (arg_private_dev) | 246 | if (arg_private_dev) |
247 | fs_private_dev(); | 247 | fs_private_dev(); |
248 | if (arg_private_etc) | ||
249 | fs_private_etc_list(); | ||
248 | 250 | ||
249 | //**************************** | 251 | //**************************** |
250 | // install trace | 252 | // install trace |
diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 71ae203ff..2beeddb70 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c | |||
@@ -152,8 +152,13 @@ void usage(void) { | |||
152 | printf("\t\tfilesystem, and copy the files and directories in the list in\n"); | 152 | printf("\t\tfilesystem, and copy the files and directories in the list in\n"); |
153 | printf("\t\tthe new home. All modifications are discarded when the sandbox\n"); | 153 | printf("\t\tthe new home. All modifications are discarded when the sandbox\n"); |
154 | printf("\t\tis closed.\n\n"); | 154 | printf("\t\tis closed.\n\n"); |
155 | |||
155 | printf("\t--private-dev - create a new /dev directory. Only null, full, zero, tty,\n"); | 156 | printf("\t--private-dev - create a new /dev directory. Only null, full, zero, tty,\n"); |
156 | printf("\t\tpst, ptms, random, urandom and shm devices are available.\n\n"); | 157 | printf("\t\tpst, ptms, random, urandom and shm devices are available.\n\n"); |
158 | |||
159 | printf("\t--private-etc=file,directory - build a new /etc in a temporary\n"); | ||
160 | printf("\t\tfilesystem, and copy the files and directories in the list.\n"); | ||
161 | printf("\t\tAll modifications are discarded when the sandbox is closed.\n\n"); | ||
157 | 162 | ||
158 | printf("\t--profile=filename - use a custom profile.\n\n"); | 163 | printf("\t--profile=filename - use a custom profile.\n\n"); |
159 | printf("\t--read-only=dirname_or_filename - set directory or file read-only.\n\n"); | 164 | printf("\t--read-only=dirname_or_filename - set directory or file read-only.\n\n"); |
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index f85e10171..60d9c47c5 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt | |||
@@ -81,6 +81,11 @@ closed. | |||
81 | .TP | 81 | .TP |
82 | \f\private-dev | 82 | \f\private-dev |
83 | Create a new /dev directory. Only null, full, zero, tty, pts, ptmx, random, urandom and shm devices are available. | 83 | Create a new /dev directory. Only null, full, zero, tty, pts, ptmx, random, urandom and shm devices are available. |
84 | .TP | ||
85 | \f\private-etc file,directory | ||
86 | Build a new /etc in a temporary | ||
87 | filesystem, and copy the files and directories in the list. | ||
88 | All modifications are discarded when the sandbox is closed. | ||
84 | 89 | ||
85 | .SH Filters | 90 | .SH Filters |
86 | \fBcaps\fR and \fBseccomp\fR enable Linux capabilities and seccomp filters. Examples: | 91 | \fBcaps\fR and \fBseccomp\fR enable Linux capabilities and seccomp filters. Examples: |
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 4e8d96d31..dbffe68ed 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -670,6 +670,19 @@ full null ptmx pts random shm tty urandom zero | |||
670 | .br | 670 | .br |
671 | $ | 671 | $ |
672 | .TP | 672 | .TP |
673 | \fB\-\-private-etc=file,directory | ||
674 | Build a new /etc in a temporary | ||
675 | filesystem, and copy the files and directories in the list. | ||
676 | All modifications are discarded when the sandbox is closed. | ||
677 | .br | ||
678 | |||
679 | .br | ||
680 | Example: | ||
681 | .br | ||
682 | $ firejail --private-etc=group,hostname,localtime, \\ | ||
683 | .br | ||
684 | nsswitch.conf,passwd,resolv.conf | ||
685 | .TP | ||
673 | \fB\-\-profile=filename | 686 | \fB\-\-profile=filename |
674 | Load a custom profile from filename. For filename use an absolute path or a path relative to the current path. | 687 | Load a custom profile from filename. For filename use an absolute path or a path relative to the current path. |
675 | For more information, see PROFILES section below. | 688 | For more information, see PROFILES section below. |