diff options
-rw-r--r-- | etc/inc/disable-common.inc | 2 | ||||
-rw-r--r-- | etc/profile-a-l/ghostwriter.profile | 2 | ||||
-rw-r--r-- | etc/profile-m-z/marker.profile | 4 | ||||
-rw-r--r-- | etc/profile-m-z/tcpdump.profile | 1 | ||||
-rw-r--r-- | src/firejail/fs_lib.c | 249 | ||||
-rw-r--r-- | src/firejail/fs_lib2.c | 42 | ||||
-rw-r--r-- | src/jailtest/jailtest.h | 4 | ||||
-rw-r--r-- | src/jailtest/main.c | 29 | ||||
-rw-r--r-- | src/jailtest/sysfiles.c | 88 | ||||
-rw-r--r-- | src/lib/ldd_utils.c | 4 |
10 files changed, 257 insertions, 168 deletions
diff --git a/etc/inc/disable-common.inc b/etc/inc/disable-common.inc index d724e3b52..52534a9e9 100644 --- a/etc/inc/disable-common.inc +++ b/etc/inc/disable-common.inc | |||
@@ -442,6 +442,7 @@ blacklist ${PATH}/mount | |||
442 | blacklist ${PATH}/mount.ecryptfs_private | 442 | blacklist ${PATH}/mount.ecryptfs_private |
443 | blacklist ${PATH}/nc | 443 | blacklist ${PATH}/nc |
444 | blacklist ${PATH}/ncat | 444 | blacklist ${PATH}/ncat |
445 | blacklist ${PATH}/nmap | ||
445 | blacklist ${PATH}/newgidmap | 446 | blacklist ${PATH}/newgidmap |
446 | blacklist ${PATH}/newgrp | 447 | blacklist ${PATH}/newgrp |
447 | blacklist ${PATH}/newuidmap | 448 | blacklist ${PATH}/newuidmap |
@@ -452,6 +453,7 @@ blacklist ${PATH}/sg | |||
452 | blacklist ${PATH}/strace | 453 | blacklist ${PATH}/strace |
453 | blacklist ${PATH}/su | 454 | blacklist ${PATH}/su |
454 | blacklist ${PATH}/sudo | 455 | blacklist ${PATH}/sudo |
456 | blacklist ${PATH}/tcpdump | ||
455 | blacklist ${PATH}/umount | 457 | blacklist ${PATH}/umount |
456 | blacklist ${PATH}/unix_chkpwd | 458 | blacklist ${PATH}/unix_chkpwd |
457 | blacklist ${PATH}/xev | 459 | blacklist ${PATH}/xev |
diff --git a/etc/profile-a-l/ghostwriter.profile b/etc/profile-a-l/ghostwriter.profile index d56d6714e..820d5e694 100644 --- a/etc/profile-a-l/ghostwriter.profile +++ b/etc/profile-a-l/ghostwriter.profile | |||
@@ -55,5 +55,5 @@ private-dev | |||
55 | private-etc alternatives,ca-certificates,crypto-policies,dbus-1,dconf,firejail,fonts,gconf,groups,gtk-2.0,gtk-3.0,host.conf,hostname,hosts,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,locale,locale.alias,locale.conf,localtime,login.defs,machine-id,mime.types,nsswitch.conf,pango,passwd,pki,protocols,resolv.conf,rpc,services,ssl,texlive,Trolltech.conf,X11,xdg | 55 | private-etc alternatives,ca-certificates,crypto-policies,dbus-1,dconf,firejail,fonts,gconf,groups,gtk-2.0,gtk-3.0,host.conf,hostname,hosts,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,locale,locale.alias,locale.conf,localtime,login.defs,machine-id,mime.types,nsswitch.conf,pango,passwd,pki,protocols,resolv.conf,rpc,services,ssl,texlive,Trolltech.conf,X11,xdg |
56 | private-tmp | 56 | private-tmp |
57 | 57 | ||
58 | dbus-user none | 58 | dbus-user filter |
59 | dbus-system none | 59 | dbus-system none |
diff --git a/etc/profile-m-z/marker.profile b/etc/profile-m-z/marker.profile index 029d0183d..70e5c72cf 100644 --- a/etc/profile-m-z/marker.profile +++ b/etc/profile-m-z/marker.profile | |||
@@ -14,6 +14,8 @@ include globals.local | |||
14 | noblacklist ${HOME}/.cache/marker | 14 | noblacklist ${HOME}/.cache/marker |
15 | noblacklist ${DOCUMENTS} | 15 | noblacklist ${DOCUMENTS} |
16 | 16 | ||
17 | include allow-python3.inc | ||
18 | |||
17 | include disable-common.inc | 19 | include disable-common.inc |
18 | include disable-devel.inc | 20 | include disable-devel.inc |
19 | include disable-exec.inc | 21 | include disable-exec.inc |
@@ -48,7 +50,7 @@ seccomp.block-secondary | |||
48 | shell none | 50 | shell none |
49 | tracelog | 51 | tracelog |
50 | 52 | ||
51 | private-bin marker | 53 | private-bin marker,python3* |
52 | private-cache | 54 | private-cache |
53 | private-dev | 55 | private-dev |
54 | private-etc alternatives,dconfgtk-3.0,fonts,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,locale,locale.alias,locale.conf,localtime,pango,X11 | 56 | private-etc alternatives,dconfgtk-3.0,fonts,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,locale,locale.alias,locale.conf,localtime,pango,X11 |
diff --git a/etc/profile-m-z/tcpdump.profile b/etc/profile-m-z/tcpdump.profile index 7984702f3..6f863d7a1 100644 --- a/etc/profile-m-z/tcpdump.profile +++ b/etc/profile-m-z/tcpdump.profile | |||
@@ -8,6 +8,7 @@ include globals.local | |||
8 | 8 | ||
9 | noblacklist /sbin | 9 | noblacklist /sbin |
10 | noblacklist /usr/sbin | 10 | noblacklist /usr/sbin |
11 | noblacklist ${PATH}/tcpdump | ||
11 | 12 | ||
12 | include disable-common.inc | 13 | include disable-common.inc |
13 | include disable-devel.inc | 14 | include disable-devel.inc |
diff --git a/src/firejail/fs_lib.c b/src/firejail/fs_lib.c index 99d57fbbb..0491fd9b1 100644 --- a/src/firejail/fs_lib.c +++ b/src/firejail/fs_lib.c | |||
@@ -23,7 +23,8 @@ | |||
23 | #include <sys/stat.h> | 23 | #include <sys/stat.h> |
24 | #include <sys/types.h> | 24 | #include <sys/types.h> |
25 | #include <unistd.h> | 25 | #include <unistd.h> |
26 | #include <dirent.h> | 26 | #include <fcntl.h> |
27 | #include <errno.h> | ||
27 | #include <glob.h> | 28 | #include <glob.h> |
28 | #define MAXBUF 4096 | 29 | #define MAXBUF 4096 |
29 | 30 | ||
@@ -34,7 +35,7 @@ extern void fslib_install_system(void); | |||
34 | static int lib_cnt = 0; | 35 | static int lib_cnt = 0; |
35 | static int dir_cnt = 0; | 36 | static int dir_cnt = 0; |
36 | 37 | ||
37 | static const char *lib_dirs[] = { | 38 | static const char *masked_lib_dirs[] = { |
38 | "/usr/lib64", | 39 | "/usr/lib64", |
39 | "/lib64", | 40 | "/lib64", |
40 | "/usr/lib", | 41 | "/usr/lib", |
@@ -44,15 +45,15 @@ static const char *lib_dirs[] = { | |||
44 | NULL, | 45 | NULL, |
45 | }; | 46 | }; |
46 | 47 | ||
47 | // return 1 if the file is in lib_dirs[] | 48 | // return 1 if the file is in masked_lib_dirs[] |
48 | static int valid_full_path(const char *full_path) { | 49 | static int valid_full_path(const char *full_path) { |
49 | if (strstr(full_path, "..")) | 50 | if (strstr(full_path, "..")) |
50 | return 0; | 51 | return 0; |
51 | 52 | ||
52 | int i = 0; | 53 | int i = 0; |
53 | while (lib_dirs[i]) { | 54 | while (masked_lib_dirs[i]) { |
54 | if (strncmp(full_path, lib_dirs[i], strlen(lib_dirs[i])) == 0 && | 55 | if (strncmp(full_path, masked_lib_dirs[i], strlen(masked_lib_dirs[i])) == 0 && |
55 | full_path[strlen(lib_dirs[i])] == '/') | 56 | full_path[strlen(masked_lib_dirs[i])] == '/') |
56 | return 1; | 57 | return 1; |
57 | i++; | 58 | i++; |
58 | } | 59 | } |
@@ -70,9 +71,10 @@ char *find_in_path(const char *program) { | |||
70 | errExit("readlink"); | 71 | errExit("readlink"); |
71 | self[len] = '\0'; | 72 | self[len] = '\0'; |
72 | 73 | ||
73 | char *path = getenv("PATH"); | 74 | const char *path = env_get("PATH"); |
74 | if (!path) | 75 | if (!path) |
75 | return NULL; | 76 | return NULL; |
77 | |||
76 | char *dup = strdup(path); | 78 | char *dup = strdup(path); |
77 | if (!dup) | 79 | if (!dup) |
78 | errExit("strdup"); | 80 | errExit("strdup"); |
@@ -105,22 +107,6 @@ char *find_in_path(const char *program) { | |||
105 | return NULL; | 107 | return NULL; |
106 | } | 108 | } |
107 | 109 | ||
108 | static void report_duplication(const char *full_path) { | ||
109 | char *fname = strrchr(full_path, '/'); | ||
110 | if (fname && *(++fname) != '\0') { | ||
111 | // report the file on all bin paths | ||
112 | int i = 0; | ||
113 | while (default_lib_paths[i]) { | ||
114 | char *p; | ||
115 | if (asprintf(&p, "%s/%s", default_lib_paths[i], fname) == -1) | ||
116 | errExit("asprintf"); | ||
117 | fs_logger2("clone", p); | ||
118 | free(p); | ||
119 | i++; | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
124 | static char *build_dest_dir(const char *full_path) { | 110 | static char *build_dest_dir(const char *full_path) { |
125 | assert(full_path); | 111 | assert(full_path); |
126 | if (strstr(full_path, "/x86_64-linux-gnu/")) | 112 | if (strstr(full_path, "/x86_64-linux-gnu/")) |
@@ -128,57 +114,107 @@ static char *build_dest_dir(const char *full_path) { | |||
128 | return RUN_LIB_DIR; | 114 | return RUN_LIB_DIR; |
129 | } | 115 | } |
130 | 116 | ||
131 | // copy fname in private_run_dir | 117 | // return name of mount target in allocated memory |
132 | void fslib_duplicate(const char *full_path) { | 118 | static char *build_dest_name(const char *full_path) { |
133 | assert(full_path); | 119 | assert(full_path); |
120 | char *fname = strrchr(full_path, '/'); | ||
121 | assert(fname); | ||
122 | fname++; | ||
123 | assert(*fname != '\0'); | ||
134 | 124 | ||
135 | struct stat s; | 125 | char *dest; |
136 | if (stat(full_path, &s) != 0 || s.st_uid != 0 || access(full_path, R_OK) | 126 | if (asprintf(&dest, "%s/%s", build_dest_dir(full_path), fname) == -1) |
137 | || !valid_full_path(full_path)) | 127 | errExit("asprintf"); |
138 | return; | 128 | return dest; |
129 | } | ||
139 | 130 | ||
140 | char *dest_dir = build_dest_dir(full_path); | 131 | static void fslib_mount_dir(const char *full_path) { |
132 | // create new directory and mount the original on top of it | ||
133 | char *dest = build_dest_name(full_path); | ||
134 | if (mkdir(dest, 0755) == -1) { | ||
135 | if (errno == EEXIST) { // directory has been mounted already, nothing to do | ||
136 | free(dest); | ||
137 | return; | ||
138 | } | ||
139 | errExit("mkdir"); | ||
140 | } | ||
141 | 141 | ||
142 | // don't copy it if the file is already there | 142 | if (arg_debug || arg_debug_private_lib) |
143 | char *ptr = strrchr(full_path, '/'); | 143 | printf(" mounting %s on %s\n", full_path, dest); |
144 | if (!ptr) | 144 | // if full_path is a symbolic link, mount will follow it |
145 | return; | 145 | if (mount(full_path, dest, NULL, MS_BIND|MS_REC, NULL) < 0) |
146 | ptr++; | 146 | errExit("mount bind"); |
147 | if (*ptr == '\0') | 147 | free(dest); |
148 | return; | 148 | dir_cnt++; |
149 | } | ||
149 | 150 | ||
150 | char *name; | 151 | static void fslib_mount_file(const char *full_path) { |
151 | if (asprintf(&name, "%s/%s", dest_dir, ptr) == -1) | 152 | // create new file and mount the original on top of it |
152 | errExit("asprintf"); | 153 | char *dest = build_dest_name(full_path); |
153 | if (stat(name, &s) == 0) { | 154 | int fd = open(dest, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWUSR); |
154 | free(name); | 155 | if (fd == -1) { |
155 | return; | 156 | if (errno == EEXIST) { // file has been mounted already, nothing to do |
157 | free(dest); | ||
158 | return; | ||
159 | } | ||
160 | errExit("open"); | ||
156 | } | 161 | } |
157 | free(name); | 162 | close(fd); |
158 | 163 | ||
159 | if (arg_debug || arg_debug_private_lib) | 164 | if (arg_debug || arg_debug_private_lib) |
160 | printf(" copying %s to private %s\n", full_path, dest_dir); | 165 | printf(" mounting %s on %s\n", full_path, dest); |
161 | 166 | // if full_path is a symbolic link, mount will follow it | |
162 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", full_path, dest_dir); | 167 | if (mount(full_path, dest, NULL, MS_BIND, NULL) < 0) |
163 | report_duplication(full_path); | 168 | errExit("mount bind"); |
169 | free(dest); | ||
164 | lib_cnt++; | 170 | lib_cnt++; |
165 | } | 171 | } |
166 | 172 | ||
173 | void fslib_mount(const char *full_path) { | ||
174 | assert(full_path); | ||
175 | struct stat s; | ||
176 | |||
177 | if (!valid_full_path(full_path) || | ||
178 | access(full_path, F_OK) != 0 || | ||
179 | stat(full_path, &s) != 0 || | ||
180 | s.st_uid != 0) | ||
181 | return; | ||
182 | |||
183 | if (S_ISDIR(s.st_mode)) | ||
184 | fslib_mount_dir(full_path); | ||
185 | else if (S_ISREG(s.st_mode) && is_lib_64(full_path)) | ||
186 | fslib_mount_file(full_path); | ||
187 | } | ||
188 | |||
167 | // requires full path for lib | 189 | // requires full path for lib |
168 | // it could be a library or an executable | 190 | // it could be a library or an executable |
169 | // lib is not copied, only libraries used by it | 191 | // lib is not copied, only libraries used by it |
170 | static void fslib_copy_libs(const char *full_path, unsigned mask) { | 192 | void fslib_mount_libs(const char *full_path, unsigned user) { |
193 | assert(full_path); | ||
194 | // if library/executable does not exist or the user does not have read access to it | ||
195 | // print a warning and exit the function. | ||
196 | if (user && access(full_path, R_OK)) { | ||
197 | if (arg_debug || arg_debug_private_lib) | ||
198 | printf("Cannot read %s, skipping...\n", full_path); | ||
199 | return; | ||
200 | } | ||
201 | |||
202 | if (arg_debug || arg_debug_private_lib) | ||
203 | printf(" fslib_mount_libs %s (parse as %s)\n", full_path, user ? "user" : "root"); | ||
171 | // create an empty RUN_LIB_FILE and allow the user to write to it | 204 | // create an empty RUN_LIB_FILE and allow the user to write to it |
172 | unlink(RUN_LIB_FILE); // in case is there | 205 | unlink(RUN_LIB_FILE); // in case is there |
173 | create_empty_file_as_root(RUN_LIB_FILE, 0644); | 206 | create_empty_file_as_root(RUN_LIB_FILE, 0644); |
174 | if (mask & SBOX_USER) { | 207 | if (user && chown(RUN_LIB_FILE, getuid(), getgid())) |
175 | if (chown(RUN_LIB_FILE, getuid(), getgid())) | 208 | errExit("chown"); |
176 | errExit("chown"); | ||
177 | } | ||
178 | 209 | ||
179 | // run fldd to extract the list of files | 210 | // run fldd to extract the list of files |
180 | if (arg_debug || arg_debug_private_lib) | 211 | if (arg_debug || arg_debug_private_lib) |
181 | printf(" running fldd %s\n", full_path); | 212 | printf(" running fldd %s\n", full_path); |
213 | unsigned mask; | ||
214 | if (user) | ||
215 | mask = SBOX_USER; | ||
216 | else | ||
217 | mask = SBOX_ROOT; | ||
182 | sbox_run(mask | SBOX_SECCOMP | SBOX_CAPS_NONE, 3, PATH_FLDD, full_path, RUN_LIB_FILE); | 218 | sbox_run(mask | SBOX_SECCOMP | SBOX_CAPS_NONE, 3, PATH_FLDD, full_path, RUN_LIB_FILE); |
183 | 219 | ||
184 | // open the list of libraries and install them on by one | 220 | // open the list of libraries and install them on by one |
@@ -192,97 +228,30 @@ static void fslib_copy_libs(const char *full_path, unsigned mask) { | |||
192 | char *ptr = strchr(buf, '\n'); | 228 | char *ptr = strchr(buf, '\n'); |
193 | if (ptr) | 229 | if (ptr) |
194 | *ptr = '\0'; | 230 | *ptr = '\0'; |
195 | fslib_duplicate(buf); | 231 | |
232 | fslib_mount(buf); | ||
196 | } | 233 | } |
197 | fclose(fp); | 234 | fclose(fp); |
198 | unlink(RUN_LIB_FILE); | 235 | unlink(RUN_LIB_FILE); |
199 | } | 236 | } |
200 | 237 | ||
201 | void fslib_copy_libs_parse_as_root(const char *full_path) { | 238 | // fname should be a valid full path at this point |
202 | assert(full_path); | ||
203 | if (arg_debug || arg_debug_private_lib) | ||
204 | printf(" fslib_copy_libs_parse_as_root %s\n", full_path); | ||
205 | |||
206 | struct stat s; | ||
207 | if (stat(full_path, &s)) { | ||
208 | if (arg_debug || arg_debug_private_lib) | ||
209 | printf("cannot find %s for private-lib, skipping...\n", full_path); | ||
210 | return; | ||
211 | } | ||
212 | fslib_copy_libs(full_path, SBOX_ROOT); | ||
213 | } | ||
214 | |||
215 | // if library/executable does not exist or the user does not have read access to it | ||
216 | // print a warning and exit the function. | ||
217 | void fslib_copy_libs_parse_as_user(const char *full_path) { | ||
218 | assert(full_path); | ||
219 | if (arg_debug || arg_debug_private_lib) | ||
220 | printf(" fslib_copy_libs_parse_as_user %s\n", full_path); | ||
221 | |||
222 | if (access(full_path, R_OK)) { | ||
223 | if (arg_debug || arg_debug_private_lib) | ||
224 | printf("cannot find %s for private-lib, skipping...\n", full_path); | ||
225 | return; | ||
226 | } | ||
227 | fslib_copy_libs(full_path, SBOX_USER); | ||
228 | } | ||
229 | |||
230 | void fslib_copy_dir(const char *full_path) { | ||
231 | assert(full_path); | ||
232 | if (arg_debug || arg_debug_private_lib) | ||
233 | printf(" fslib_copy_dir %s\n", full_path); | ||
234 | |||
235 | // do nothing if the directory does not exist or is not owned by root | ||
236 | struct stat s; | ||
237 | if (stat(full_path, &s) != 0 || s.st_uid != 0 || !S_ISDIR(s.st_mode) || access(full_path, R_OK) | ||
238 | || !valid_full_path(full_path)) | ||
239 | return; | ||
240 | |||
241 | char *dir_name = strrchr(full_path, '/'); | ||
242 | assert(dir_name); | ||
243 | dir_name++; | ||
244 | assert(*dir_name != '\0'); | ||
245 | |||
246 | // do nothing if the directory is already there | ||
247 | char *dest; | ||
248 | if (asprintf(&dest, "%s/%s", build_dest_dir(full_path), dir_name) == -1) | ||
249 | errExit("asprintf"); | ||
250 | if (stat(dest, &s) == 0) { | ||
251 | free(dest); | ||
252 | return; | ||
253 | } | ||
254 | |||
255 | // create new directory and mount the original on top of it | ||
256 | mkdir_attr(dest, 0755, 0, 0); | ||
257 | |||
258 | if (mount(full_path, dest, NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
259 | mount(NULL, dest, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
260 | errExit("mount bind"); | ||
261 | fs_logger2("clone", full_path); | ||
262 | fs_logger2("mount", full_path); | ||
263 | dir_cnt++; | ||
264 | free(dest); | ||
265 | } | ||
266 | |||
267 | // fname should be a vallid full path at this point | ||
268 | static void load_library(const char *fname) { | 239 | static void load_library(const char *fname) { |
269 | assert(fname); | 240 | assert(fname); |
270 | assert(*fname == '/'); | 241 | assert(*fname == '/'); |
271 | 242 | ||
272 | // existing file owned by root, read access | 243 | // existing file owned by root |
273 | struct stat s; | 244 | struct stat s; |
274 | if (stat(fname, &s) == 0 && s.st_uid == 0 && !access(fname, R_OK)) { | 245 | if (!access(fname, F_OK) && stat(fname, &s) == 0 && s.st_uid == 0) { |
275 | // load directories, regular 64 bit libraries, and 64 bit executables | 246 | // load directories, regular 64 bit libraries, and 64 bit executables |
276 | if (is_dir(fname) || is_lib_64(fname)) { | 247 | if (S_ISDIR(s.st_mode)) |
277 | if (is_dir(fname)) | 248 | fslib_mount(fname); |
278 | fslib_copy_dir(fname); | 249 | else if (S_ISREG(s.st_mode) && is_lib_64(fname)) { |
279 | else { | 250 | if (strstr(fname, ".so") || |
280 | if (strstr(fname, ".so") || | 251 | access(fname, X_OK) != 0) // don't duplicate executables, just install the libraries |
281 | access(fname, X_OK) != 0) // don't duplicate executables, just install the libraries | 252 | fslib_mount(fname); |
282 | fslib_duplicate(fname); | 253 | |
283 | 254 | fslib_mount_libs(fname, 1); // parse as user | |
284 | fslib_copy_libs_parse_as_user(fname); | ||
285 | } | ||
286 | } | 255 | } |
287 | } | 256 | } |
288 | } | 257 | } |
@@ -338,7 +307,6 @@ static void install_list_entry(const char *lib) { | |||
338 | return; | 307 | return; |
339 | } | 308 | } |
340 | 309 | ||
341 | |||
342 | void fslib_install_list(const char *lib_list) { | 310 | void fslib_install_list(const char *lib_list) { |
343 | assert(lib_list); | 311 | assert(lib_list); |
344 | if (arg_debug || arg_debug_private_lib) | 312 | if (arg_debug || arg_debug_private_lib) |
@@ -365,14 +333,14 @@ static void mount_directories(void) { | |||
365 | fs_remount(RUN_LIB_DIR, MOUNT_READONLY, 1); // should be redundant except for RUN_LIB_DIR itself | 333 | fs_remount(RUN_LIB_DIR, MOUNT_READONLY, 1); // should be redundant except for RUN_LIB_DIR itself |
366 | 334 | ||
367 | int i = 0; | 335 | int i = 0; |
368 | while (lib_dirs[i]) { | 336 | while (masked_lib_dirs[i]) { |
369 | if (is_dir(lib_dirs[i])) { | 337 | if (is_dir(masked_lib_dirs[i])) { |
370 | if (arg_debug || arg_debug_private_lib) | 338 | if (arg_debug || arg_debug_private_lib) |
371 | printf("Mount-bind %s on top of %s\n", RUN_LIB_DIR, lib_dirs[i]); | 339 | printf("Mount-bind %s on top of %s\n", RUN_LIB_DIR, masked_lib_dirs[i]); |
372 | if (mount(RUN_LIB_DIR, lib_dirs[i], NULL, MS_BIND|MS_REC, NULL) < 0) | 340 | if (mount(RUN_LIB_DIR, masked_lib_dirs[i], NULL, MS_BIND|MS_REC, NULL) < 0) |
373 | errExit("mount bind"); | 341 | errExit("mount bind"); |
374 | fs_logger2("tmpfs", lib_dirs[i]); | 342 | fs_logger2("tmpfs", masked_lib_dirs[i]); |
375 | fs_logger2("mount", lib_dirs[i]); | 343 | fs_logger2("mount", masked_lib_dirs[i]); |
376 | } | 344 | } |
377 | i++; | 345 | i++; |
378 | } | 346 | } |
@@ -444,7 +412,6 @@ void fs_private_lib(void) { | |||
444 | fslib_install_list(cfg.shell); | 412 | fslib_install_list(cfg.shell); |
445 | // a shell is useless without some basic commands | 413 | // a shell is useless without some basic commands |
446 | fslib_install_list("/bin/ls,/bin/cat,/bin/mv,/bin/rm"); | 414 | fslib_install_list("/bin/ls,/bin/cat,/bin/mv,/bin/rm"); |
447 | |||
448 | } | 415 | } |
449 | 416 | ||
450 | // for the listed libs and directories | 417 | // for the listed libs and directories |
diff --git a/src/firejail/fs_lib2.c b/src/firejail/fs_lib2.c index d46cfed86..c69bf7c98 100644 --- a/src/firejail/fs_lib2.c +++ b/src/firejail/fs_lib2.c | |||
@@ -21,10 +21,8 @@ | |||
21 | #include <dirent.h> | 21 | #include <dirent.h> |
22 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
23 | 23 | ||
24 | extern void fslib_duplicate(const char *full_path); | 24 | extern void fslib_mount_libs(const char *full_path, unsigned user); |
25 | extern void fslib_copy_libs_parse_as_user(const char *full_path); | 25 | extern void fslib_mount(const char *full_path); |
26 | extern void fslib_copy_libs_parse_as_root(const char *full_path); | ||
27 | extern void fslib_copy_dir(const char *full_path); | ||
28 | 26 | ||
29 | //*************************************************************** | 27 | //*************************************************************** |
30 | // Standard C library | 28 | // Standard C library |
@@ -98,7 +96,8 @@ static void stdc(const char *dirname) { | |||
98 | if (asprintf(&fname, "%s/%s", dirname, entry->d_name) == -1) | 96 | if (asprintf(&fname, "%s/%s", dirname, entry->d_name) == -1) |
99 | errExit("asprintf"); | 97 | errExit("asprintf"); |
100 | 98 | ||
101 | fslib_duplicate(fname); | 99 | fslib_mount(fname); |
100 | free(fname); | ||
102 | } | 101 | } |
103 | } | 102 | } |
104 | closedir(dir); | 103 | closedir(dir); |
@@ -119,7 +118,7 @@ void fslib_install_stdc(void) { | |||
119 | 118 | ||
120 | // install locale | 119 | // install locale |
121 | if (stat("/usr/lib/locale", &s) == 0) | 120 | if (stat("/usr/lib/locale", &s) == 0) |
122 | fslib_copy_dir("/usr/lib/locale"); | 121 | fslib_mount("/usr/lib/locale"); |
123 | 122 | ||
124 | fmessage("Standard C library installed in %0.2f ms\n", timetrace_end()); | 123 | fmessage("Standard C library installed in %0.2f ms\n", timetrace_end()); |
125 | } | 124 | } |
@@ -129,7 +128,8 @@ void fslib_install_stdc(void) { | |||
129 | //*************************************************************** | 128 | //*************************************************************** |
130 | 129 | ||
131 | static void fdir(void) { | 130 | static void fdir(void) { |
132 | fslib_copy_dir(LIBDIR "/firejail"); | 131 | // firejail directory itself |
132 | fslib_mount(LIBDIR "/firejail"); | ||
133 | 133 | ||
134 | // executables and libraries from firejail directory | 134 | // executables and libraries from firejail directory |
135 | static const char * const fbin[] = { | 135 | static const char * const fbin[] = { |
@@ -143,30 +143,28 @@ static void fdir(void) { | |||
143 | NULL, | 143 | NULL, |
144 | }; | 144 | }; |
145 | 145 | ||
146 | // need to run fldd as root user, unprivileged users have no read permission on executables | 146 | // need to parse as root user, unprivileged users have no read permission on executables |
147 | int i; | 147 | int i; |
148 | for (i = 0; fbin[i]; i++) | 148 | for (i = 0; fbin[i]; i++) |
149 | fslib_copy_libs_parse_as_root(fbin[i]); | 149 | fslib_mount_libs(fbin[i], 0); |
150 | } | 150 | } |
151 | 151 | ||
152 | void fslib_install_firejail(void) { | 152 | void fslib_install_firejail(void) { |
153 | timetrace_start(); | 153 | timetrace_start(); |
154 | // bring in firejail executable libraries, in case we are redirected here | 154 | // bring in firejail executable libraries, in case we are redirected here |
155 | // by a firejail symlink from /usr/local/bin/firejail | 155 | // by a firejail symlink from /usr/local/bin/firejail |
156 | fslib_copy_libs_parse_as_user(PATH_FIREJAIL); | 156 | fslib_mount_libs(PATH_FIREJAIL, 1); // parse as user |
157 | 157 | ||
158 | // bring in firejail directory | 158 | // bring in firejail directory |
159 | fdir(); | 159 | fdir(); |
160 | 160 | ||
161 | // bring in dhclient libraries | 161 | // bring in dhclient libraries |
162 | if (any_dhcp()) | 162 | if (any_dhcp()) |
163 | fslib_copy_libs_parse_as_user(RUN_MNT_DIR "/dhclient"); | 163 | fslib_mount_libs(RUN_MNT_DIR "/dhclient", 1); // parse as user |
164 | 164 | ||
165 | #ifdef HAVE_X11 | ||
166 | // bring in xauth libraries | 165 | // bring in xauth libraries |
167 | if (arg_x11_xorg) | 166 | if (arg_x11_xorg) |
168 | fslib_copy_libs_parse_as_user("/usr/bin/xauth"); | 167 | fslib_mount_libs("/usr/bin/xauth", 1); // parse as user |
169 | #endif | ||
170 | 168 | ||
171 | fmessage("Firejail libraries installed in %0.2f ms\n", timetrace_end()); | 169 | fmessage("Firejail libraries installed in %0.2f ms\n", timetrace_end()); |
172 | } | 170 | } |
@@ -315,8 +313,8 @@ void fslib_install_system(void) { | |||
315 | if (asprintf(&name, "/usr/lib/x86_64-linux-gnu/%s", ptr->dir1) == -1) | 313 | if (asprintf(&name, "/usr/lib/x86_64-linux-gnu/%s", ptr->dir1) == -1) |
316 | errExit("asprintf"); | 314 | errExit("asprintf"); |
317 | if (access(name, R_OK) == 0) { | 315 | if (access(name, R_OK) == 0) { |
318 | fslib_copy_libs_parse_as_user(name); | 316 | fslib_mount_libs(name, 1); // parse as user |
319 | fslib_copy_dir(name); | 317 | fslib_mount(name); |
320 | } | 318 | } |
321 | else { | 319 | else { |
322 | free(name); | 320 | free(name); |
@@ -324,8 +322,8 @@ void fslib_install_system(void) { | |||
324 | if (asprintf(&name, "/usr/lib64/%s", ptr->dir1) == -1) | 322 | if (asprintf(&name, "/usr/lib64/%s", ptr->dir1) == -1) |
325 | errExit("asprintf"); | 323 | errExit("asprintf"); |
326 | if (access(name, R_OK) == 0) { | 324 | if (access(name, R_OK) == 0) { |
327 | fslib_copy_libs_parse_as_user(name); | 325 | fslib_mount_libs(name, 1); // parse as user |
328 | fslib_copy_dir(name); | 326 | fslib_mount(name); |
329 | } | 327 | } |
330 | } | 328 | } |
331 | free(name); | 329 | free(name); |
@@ -335,8 +333,8 @@ void fslib_install_system(void) { | |||
335 | if (asprintf(&name, "/usr/lib/x86_64-linux-gnu/%s", ptr->dir2) == -1) | 333 | if (asprintf(&name, "/usr/lib/x86_64-linux-gnu/%s", ptr->dir2) == -1) |
336 | errExit("asprintf"); | 334 | errExit("asprintf"); |
337 | if (access(name, R_OK) == 0) { | 335 | if (access(name, R_OK) == 0) { |
338 | fslib_copy_libs_parse_as_user(name); | 336 | fslib_mount_libs(name, 1); // parse as user |
339 | fslib_copy_dir(name); | 337 | fslib_mount(name); |
340 | } | 338 | } |
341 | else { | 339 | else { |
342 | free(name); | 340 | free(name); |
@@ -344,8 +342,8 @@ void fslib_install_system(void) { | |||
344 | if (asprintf(&name, "/usr/lib64/%s", ptr->dir2) == -1) | 342 | if (asprintf(&name, "/usr/lib64/%s", ptr->dir2) == -1) |
345 | errExit("asprintf"); | 343 | errExit("asprintf"); |
346 | if (access(name, R_OK) == 0) { | 344 | if (access(name, R_OK) == 0) { |
347 | fslib_copy_libs_parse_as_user(name); | 345 | fslib_mount_libs(name, 1); // parse as user |
348 | fslib_copy_dir(name); | 346 | fslib_mount(name); |
349 | } | 347 | } |
350 | } | 348 | } |
351 | free(name); | 349 | free(name); |
diff --git a/src/jailtest/jailtest.h b/src/jailtest/jailtest.h index 10174cc9a..0c4883061 100644 --- a/src/jailtest/jailtest.h +++ b/src/jailtest/jailtest.h | |||
@@ -38,6 +38,10 @@ void access_destroy(void); | |||
38 | void noexec_setup(void); | 38 | void noexec_setup(void); |
39 | void noexec_test(const char *msg); | 39 | void noexec_test(const char *msg); |
40 | 40 | ||
41 | // sysfiles.c | ||
42 | void sysfiles_setup(const char *file); | ||
43 | void sysfiles_test(void); | ||
44 | |||
41 | // virtual.c | 45 | // virtual.c |
42 | void virtual_setup(const char *directory); | 46 | void virtual_setup(const char *directory); |
43 | void virtual_destroy(void); | 47 | void virtual_destroy(void); |
diff --git a/src/jailtest/main.c b/src/jailtest/main.c index 850277bc5..3369dca39 100644 --- a/src/jailtest/main.c +++ b/src/jailtest/main.c | |||
@@ -114,8 +114,32 @@ int main(int argc, char **argv) { | |||
114 | virtual_setup("/bin"); | 114 | virtual_setup("/bin"); |
115 | virtual_setup("/usr/share"); | 115 | virtual_setup("/usr/share"); |
116 | virtual_setup(user_run_dir); | 116 | virtual_setup(user_run_dir); |
117 | 117 | // basic sysfiles | |
118 | 118 | sysfiles_setup("/etc/shadow"); | |
119 | sysfiles_setup("/etc/gshadow"); | ||
120 | sysfiles_setup("/usr/bin/mount"); | ||
121 | sysfiles_setup("/usr/bin/su"); | ||
122 | sysfiles_setup("/usr/bin/ksu"); | ||
123 | sysfiles_setup("/usr/bin/sudo"); | ||
124 | sysfiles_setup("/usr/bin/strace"); | ||
125 | // X11 | ||
126 | sysfiles_setup("/usr/bin/xev"); | ||
127 | sysfiles_setup("/usr/bin/xinput"); | ||
128 | // compilers | ||
129 | sysfiles_setup("/usr/bin/gcc"); | ||
130 | sysfiles_setup("/usr/bin/clang"); | ||
131 | // networking | ||
132 | sysfiles_setup("/usr/bin/dig"); | ||
133 | sysfiles_setup("/usr/bin/nslookup"); | ||
134 | sysfiles_setup("/usr/bin/resolvectl"); | ||
135 | sysfiles_setup("/usr/bin/nc"); | ||
136 | sysfiles_setup("/usr/bin/ncat"); | ||
137 | sysfiles_setup("/usr/bin/nmap"); | ||
138 | sysfiles_setup("/usr/sbin/tcpdump"); | ||
139 | // terminals | ||
140 | sysfiles_setup("/usr/bin/gnome-terminal"); | ||
141 | sysfiles_setup("/usr/bin/xfce4-terminal"); | ||
142 | sysfiles_setup("/usr/bin/lxterminal"); | ||
119 | 143 | ||
120 | // print processes | 144 | // print processes |
121 | pid_read(0); | 145 | pid_read(0); |
@@ -145,6 +169,7 @@ int main(int argc, char **argv) { | |||
145 | noexec_test("/var/tmp"); | 169 | noexec_test("/var/tmp"); |
146 | noexec_test(user_run_dir); | 170 | noexec_test(user_run_dir); |
147 | access_test(); | 171 | access_test(); |
172 | sysfiles_test(); | ||
148 | } | 173 | } |
149 | else { | 174 | else { |
150 | printf(" Error: I cannot join the process mount space\n"); | 175 | printf(" Error: I cannot join the process mount space\n"); |
diff --git a/src/jailtest/sysfiles.c b/src/jailtest/sysfiles.c new file mode 100644 index 000000000..7e4709453 --- /dev/null +++ b/src/jailtest/sysfiles.c | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2021 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 "jailtest.h" | ||
21 | #include <dirent.h> | ||
22 | #include <sys/wait.h> | ||
23 | |||
24 | typedef struct { | ||
25 | char *tfile; | ||
26 | } TestFile; | ||
27 | |||
28 | #define MAX_TEST_FILES 32 | ||
29 | TestFile tf[MAX_TEST_FILES]; | ||
30 | static int files_cnt = 0; | ||
31 | |||
32 | void sysfiles_setup(const char *file) { | ||
33 | // I am root! | ||
34 | assert(file); | ||
35 | |||
36 | if (files_cnt >= MAX_TEST_FILES) { | ||
37 | fprintf(stderr, "Error: maximum number of system test files exceded\n"); | ||
38 | exit(1); | ||
39 | } | ||
40 | |||
41 | if (access(file, F_OK)) { | ||
42 | // no such file | ||
43 | return; | ||
44 | } | ||
45 | |||
46 | |||
47 | char *fname = strdup(file); | ||
48 | if (!fname) | ||
49 | errExit("strdup"); | ||
50 | |||
51 | tf[files_cnt].tfile = fname; | ||
52 | files_cnt++; | ||
53 | } | ||
54 | |||
55 | void sysfiles_test(void) { | ||
56 | // I am root in sandbox mount namespace | ||
57 | assert(user_uid); | ||
58 | int i; | ||
59 | |||
60 | pid_t child = fork(); | ||
61 | if (child == -1) | ||
62 | errExit("fork"); | ||
63 | |||
64 | if (child == 0) { // child | ||
65 | // drop privileges | ||
66 | if (setgid(user_gid) != 0) | ||
67 | errExit("setgid"); | ||
68 | if (setuid(user_uid) != 0) | ||
69 | errExit("setuid"); | ||
70 | |||
71 | for (i = 0; i < files_cnt; i++) { | ||
72 | assert(tf[i].tfile); | ||
73 | |||
74 | // try to open the file for reading | ||
75 | FILE *fp = fopen(tf[i].tfile, "r"); | ||
76 | if (fp) { | ||
77 | |||
78 | printf(" Warning: I can access %s\n", tf[i].tfile); | ||
79 | fclose(fp); | ||
80 | } | ||
81 | } | ||
82 | exit(0); | ||
83 | } | ||
84 | |||
85 | // wait for the child to finish | ||
86 | int status; | ||
87 | wait(&status); | ||
88 | } | ||
diff --git a/src/lib/ldd_utils.c b/src/lib/ldd_utils.c index 43fee4f21..cd60d74e4 100644 --- a/src/lib/ldd_utils.c +++ b/src/lib/ldd_utils.c | |||
@@ -23,12 +23,14 @@ | |||
23 | #include <sys/stat.h> | 23 | #include <sys/stat.h> |
24 | #include <fcntl.h> | 24 | #include <fcntl.h> |
25 | 25 | ||
26 | // todo: resolve overlap with masked_lib_dirs[] array from fs_lib.c | ||
26 | const char * const default_lib_paths[] = { | 27 | const char * const default_lib_paths[] = { |
27 | "/usr/lib/x86_64-linux-gnu", // Debian & friends | 28 | "/usr/lib/x86_64-linux-gnu", // Debian & friends |
28 | "/lib/x86_64-linux-gnu", // CentOS, Fedora | 29 | "/lib/x86_64-linux-gnu", // CentOS, Fedora |
30 | "/usr/lib64", | ||
31 | "/lib64", | ||
29 | "/usr/lib", | 32 | "/usr/lib", |
30 | "/lib", | 33 | "/lib", |
31 | "/lib64", | ||
32 | LIBDIR, | 34 | LIBDIR, |
33 | "/usr/local/lib64", | 35 | "/usr/local/lib64", |
34 | "/usr/local/lib", | 36 | "/usr/local/lib", |