diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/firejail.h | 3 | ||||
-rw-r--r-- | src/firejail/ls.c | 169 | ||||
-rw-r--r-- | src/firejail/main.c | 31 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 40 | ||||
-rw-r--r-- | src/firejail/usage.c | 3 | ||||
-rw-r--r-- | src/man/firejail.txt | 20 |
6 files changed, 174 insertions, 92 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index c98f80d13..49d19e33d 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -728,10 +728,13 @@ void x11_xorg(void); | |||
728 | // ls.c | 728 | // ls.c |
729 | enum { | 729 | enum { |
730 | SANDBOX_FS_LS = 0, | 730 | SANDBOX_FS_LS = 0, |
731 | SANDBOX_FS_CAT, | ||
731 | SANDBOX_FS_GET, | 732 | SANDBOX_FS_GET, |
732 | SANDBOX_FS_PUT, | 733 | SANDBOX_FS_PUT, |
733 | SANDBOX_FS_MAX // this should always be the last entry | 734 | SANDBOX_FS_MAX // this should always be the last entry |
734 | }; | 735 | }; |
736 | void ls(const char *path); | ||
737 | void cat(const char *path); | ||
735 | void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) __attribute__((noreturn)); | 738 | void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) __attribute__((noreturn)); |
736 | 739 | ||
737 | // checkcfg.c | 740 | // checkcfg.c |
diff --git a/src/firejail/ls.c b/src/firejail/ls.c index aa33d838b..4d0a001b6 100644 --- a/src/firejail/ls.c +++ b/src/firejail/ls.c | |||
@@ -34,18 +34,12 @@ | |||
34 | static uid_t c_uid = 0; | 34 | static uid_t c_uid = 0; |
35 | static char *c_uid_name = NULL; | 35 | static char *c_uid_name = NULL; |
36 | 36 | ||
37 | static void print_file_or_dir(const char *path, const char *fname, int separator) { | 37 | static void print_file_or_dir(const char *path, const char *fname) { |
38 | assert(fname); | 38 | assert(fname); |
39 | 39 | ||
40 | char *name; | 40 | char *name; |
41 | if (separator) { | 41 | if (asprintf(&name, "%s/%s", path, fname) == -1) |
42 | if (asprintf(&name, "%s/%s", path, fname) == -1) | 42 | errExit("asprintf"); |
43 | errExit("asprintf"); | ||
44 | } | ||
45 | else { | ||
46 | if (asprintf(&name, "%s%s", path, fname) == -1) | ||
47 | errExit("asprintf"); | ||
48 | } | ||
49 | 43 | ||
50 | struct stat s; | 44 | struct stat s; |
51 | if (stat(name, &s) == -1) { | 45 | if (stat(name, &s) == -1) { |
@@ -178,13 +172,81 @@ static void print_directory(const char *path) { | |||
178 | errExit("scandir"); | 172 | errExit("scandir"); |
179 | else { | 173 | else { |
180 | for (i = 0; i < n; i++) { | 174 | for (i = 0; i < n; i++) { |
181 | print_file_or_dir(path, namelist[i]->d_name, 0); | 175 | print_file_or_dir(path, namelist[i]->d_name); |
182 | free(namelist[i]); | 176 | free(namelist[i]); |
183 | } | 177 | } |
184 | } | 178 | } |
185 | free(namelist); | 179 | free(namelist); |
186 | } | 180 | } |
187 | 181 | ||
182 | void ls(const char *path) { | ||
183 | EUID_ASSERT(); | ||
184 | assert(path); | ||
185 | |||
186 | char *rp = realpath(path, NULL); | ||
187 | if (!rp || access(rp, R_OK) == -1) { | ||
188 | fprintf(stderr, "Error: cannot access %s\n", path); | ||
189 | exit(1); | ||
190 | } | ||
191 | if (arg_debug) | ||
192 | printf("ls %s\n", rp); | ||
193 | |||
194 | // list directory contents | ||
195 | struct stat s; | ||
196 | if (stat(rp, &s) == -1) { | ||
197 | fprintf(stderr, "Error: cannot access %s\n", rp); | ||
198 | exit(1); | ||
199 | } | ||
200 | if (S_ISDIR(s.st_mode)) | ||
201 | print_directory(rp); | ||
202 | else { | ||
203 | char *split = strrchr(rp, '/'); | ||
204 | if (split) { | ||
205 | *split = '\0'; | ||
206 | char *rp2 = split + 1; | ||
207 | if (arg_debug) | ||
208 | printf("path %s, file %s\n", rp, rp2); | ||
209 | print_file_or_dir(rp, rp2); | ||
210 | } | ||
211 | } | ||
212 | free(rp); | ||
213 | } | ||
214 | |||
215 | void cat(const char *path) { | ||
216 | EUID_ASSERT(); | ||
217 | assert(path); | ||
218 | |||
219 | if (arg_debug) | ||
220 | printf("cat %s\n", path); | ||
221 | FILE *fp = fopen(path, "r"); | ||
222 | if (!fp) { | ||
223 | fprintf(stderr, "Error: cannot read %s\n", path); | ||
224 | exit(1); | ||
225 | } | ||
226 | int fd = fileno(fp); | ||
227 | if (fd == -1) | ||
228 | errExit("fileno"); | ||
229 | struct stat s; | ||
230 | if (fstat(fd, &s) == -1) | ||
231 | errExit("fstat"); | ||
232 | if (!S_ISREG(s.st_mode)) { | ||
233 | fprintf(stderr, "Error: %s is not a regular file\n", path); | ||
234 | exit(1); | ||
235 | } | ||
236 | bool tty = isatty(STDOUT_FILENO); | ||
237 | |||
238 | int c; | ||
239 | while ((c = fgetc(fp)) != EOF) { | ||
240 | // file is untrusted | ||
241 | // replace control characters when printing to a terminal | ||
242 | if (tty && c != '\t' && c != '\n' && iscntrl((unsigned char) c)) | ||
243 | c = '?'; | ||
244 | fputc(c, stdout); | ||
245 | } | ||
246 | fflush(stdout); | ||
247 | fclose(fp); | ||
248 | } | ||
249 | |||
188 | char *expand_path(const char *path) { | 250 | char *expand_path(const char *path) { |
189 | char *fname = NULL; | 251 | char *fname = NULL; |
190 | if (*path == '/') { | 252 | if (*path == '/') { |
@@ -219,14 +281,14 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) { | |||
219 | check_join_permission(pid); | 281 | check_join_permission(pid); |
220 | 282 | ||
221 | // expand paths | 283 | // expand paths |
222 | char *fname1 = expand_path(path1);; | 284 | char *fname1 = expand_path(path1); |
223 | char *fname2 = NULL; | 285 | char *fname2 = NULL; |
224 | if (path2 != NULL) { | 286 | if (path2 != NULL) { |
225 | fname2 = expand_path(path2); | 287 | fname2 = expand_path(path2); |
226 | } | 288 | } |
227 | if (arg_debug) { | 289 | if (arg_debug) { |
228 | printf("file1 %s\n", fname1); | 290 | printf("file1 %s\n", fname1); |
229 | printf("file2 %s\n", fname2); | 291 | printf("file2 %s\n", fname2 ? fname2 : "(null)"); |
230 | } | 292 | } |
231 | 293 | ||
232 | // sandbox root directory | 294 | // sandbox root directory |
@@ -234,57 +296,36 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) { | |||
234 | if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) | 296 | if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) |
235 | errExit("asprintf"); | 297 | errExit("asprintf"); |
236 | 298 | ||
237 | if (op == SANDBOX_FS_LS) { | 299 | if (op == SANDBOX_FS_LS || op == SANDBOX_FS_CAT) { |
238 | EUID_ROOT(); | 300 | pid_t child = fork(); |
239 | // chroot | 301 | if (child < 0) |
240 | if (chroot(rootdir) < 0) | 302 | errExit("fork"); |
241 | errExit("chroot"); | 303 | if (child == 0) { |
242 | if (chdir("/") < 0) | 304 | EUID_ROOT(); |
243 | errExit("chdir"); | 305 | // chroot |
244 | 306 | if (chroot(rootdir) < 0) | |
245 | // drop privileges | 307 | errExit("chroot"); |
246 | drop_privs(0); | 308 | if (chdir("/") < 0) |
247 | 309 | errExit("chdir"); | |
248 | // check access | ||
249 | if (access(fname1, R_OK) == -1) { | ||
250 | fprintf(stderr, "Error: Cannot access %s\n", fname1); | ||
251 | exit(1); | ||
252 | } | ||
253 | /* coverity[toctou] */ | ||
254 | char *rp = realpath(fname1, NULL); | ||
255 | if (!rp) { | ||
256 | fprintf(stderr, "Error: Cannot access %s\n", fname1); | ||
257 | exit(1); | ||
258 | } | ||
259 | if (arg_debug) | ||
260 | printf("realpath %s\n", rp); | ||
261 | |||
262 | 310 | ||
263 | // list directory contents | 311 | // drop privileges |
264 | struct stat s; | 312 | drop_privs(0); |
265 | if (stat(rp, &s) == -1) { | ||
266 | fprintf(stderr, "Error: Cannot access %s\n", rp); | ||
267 | exit(1); | ||
268 | } | ||
269 | if (S_ISDIR(s.st_mode)) { | ||
270 | char *dir; | ||
271 | if (asprintf(&dir, "%s/", rp) == -1) | ||
272 | errExit("asprintf"); | ||
273 | 313 | ||
274 | print_directory(dir); | 314 | if (op == SANDBOX_FS_LS) |
275 | free(dir); | 315 | ls(fname1); |
276 | } | 316 | else |
277 | else { | 317 | cat(fname1); |
278 | char *split = strrchr(rp, '/'); | 318 | #ifdef HAVE_GCOV |
279 | if (split) { | 319 | __gcov_flush(); |
280 | *split = '\0'; | 320 | #endif |
281 | char *rp2 = split + 1; | 321 | _exit(0); |
282 | if (arg_debug) | ||
283 | printf("path %s, file %s\n", rp, rp2); | ||
284 | print_file_or_dir(rp, rp2, 1); | ||
285 | } | ||
286 | } | 322 | } |
287 | free(rp); | 323 | // wait for the child to finish |
324 | int status = 0; | ||
325 | waitpid(child, &status, 0); | ||
326 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0); | ||
327 | else | ||
328 | exit(1); | ||
288 | } | 329 | } |
289 | 330 | ||
290 | // get file from sandbox and store it in the current directory | 331 | // get file from sandbox and store it in the current directory |
@@ -303,10 +344,12 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) { | |||
303 | // create a user-owned temporary file in /run/firejail directory | 344 | // create a user-owned temporary file in /run/firejail directory |
304 | char tmp_fname[] = "/run/firejail/tmpget-XXXXXX"; | 345 | char tmp_fname[] = "/run/firejail/tmpget-XXXXXX"; |
305 | int fd = mkstemp(tmp_fname); | 346 | int fd = mkstemp(tmp_fname); |
306 | if (fd != -1) { | 347 | if (fd == -1) { |
307 | SET_PERMS_FD(fd, getuid(), getgid(), 0600); | 348 | fprintf(stderr, "Error: cannot create temporary file %s\n", tmp_fname); |
308 | close(fd); | 349 | exit(1); |
309 | } | 350 | } |
351 | SET_PERMS_FD(fd, getuid(), getgid(), 0600); | ||
352 | close(fd); | ||
310 | 353 | ||
311 | // copy the source file into the temporary file - we need to chroot | 354 | // copy the source file into the temporary file - we need to chroot |
312 | pid_t child = fork(); | 355 | pid_t child = fork(); |
diff --git a/src/firejail/main.c b/src/firejail/main.c index b9cb43444..412c6148a 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -811,6 +811,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
811 | } | 811 | } |
812 | 812 | ||
813 | // list directory contents | 813 | // list directory contents |
814 | if (!arg_debug) | ||
815 | arg_quiet = 1; | ||
814 | pid_t pid = require_pid(argv[i] + 5); | 816 | pid_t pid = require_pid(argv[i] + 5); |
815 | sandboxfs(SANDBOX_FS_LS, pid, path, NULL); | 817 | sandboxfs(SANDBOX_FS_LS, pid, path, NULL); |
816 | exit(0); | 818 | exit(0); |
@@ -818,6 +820,35 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
818 | else | 820 | else |
819 | exit_err_feature("file transfer"); | 821 | exit_err_feature("file transfer"); |
820 | } | 822 | } |
823 | else if (strncmp(argv[i], "--cat=", 6) == 0) { | ||
824 | if (checkcfg(CFG_FILE_TRANSFER)) { | ||
825 | logargs(argc, argv); | ||
826 | if (arg_private_cwd) { | ||
827 | fprintf(stderr, "Error: --cat and --private-cwd options are mutually exclusive\n"); | ||
828 | exit(1); | ||
829 | } | ||
830 | |||
831 | if ((i + 2) != argc) { | ||
832 | fprintf(stderr, "Error: invalid --cat option, path expected\n"); | ||
833 | exit(1); | ||
834 | } | ||
835 | char *path = argv[i + 1]; | ||
836 | invalid_filename(path, 0); // no globbing | ||
837 | if (strstr(path, "..")) { | ||
838 | fprintf(stderr, "Error: invalid file name %s\n", path); | ||
839 | exit(1); | ||
840 | } | ||
841 | |||
842 | // write file contents to stdout | ||
843 | if (!arg_debug) | ||
844 | arg_quiet = 1; | ||
845 | pid_t pid = require_pid(argv[i] + 6); | ||
846 | sandboxfs(SANDBOX_FS_CAT, pid, path, NULL); | ||
847 | exit(0); | ||
848 | } | ||
849 | else | ||
850 | exit_err_feature("file transfer"); | ||
851 | } | ||
821 | #endif | 852 | #endif |
822 | else if (strncmp(argv[i], "--join=", 7) == 0) { | 853 | else if (strncmp(argv[i], "--join=", 7) == 0) { |
823 | if (checkcfg(CFG_JOIN) || getuid() == 0) { | 854 | if (checkcfg(CFG_JOIN) || getuid() == 0) { |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index e42d35be5..81d535762 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -140,6 +140,20 @@ void set_apparmor(void) { | |||
140 | } | 140 | } |
141 | #endif | 141 | #endif |
142 | 142 | ||
143 | #ifdef HAVE_SECCOMP | ||
144 | void seccomp_debug(void) { | ||
145 | if (arg_debug == 0) | ||
146 | return; | ||
147 | |||
148 | EUID_USER(); | ||
149 | printf("Seccomp directory:\n"); | ||
150 | ls(RUN_SECCOMP_DIR); | ||
151 | printf("Active seccomp files:\n"); | ||
152 | cat(RUN_SECCOMP_LIST); | ||
153 | EUID_ROOT(); | ||
154 | } | ||
155 | #endif | ||
156 | |||
143 | static void save_nogroups(void) { | 157 | static void save_nogroups(void) { |
144 | if (arg_nogroups == 0) | 158 | if (arg_nogroups == 0) |
145 | return; | 159 | return; |
@@ -197,32 +211,6 @@ static FILE *create_ready_for_join_file(void) { | |||
197 | } | 211 | } |
198 | } | 212 | } |
199 | 213 | ||
200 | #ifdef HAVE_SECCOMP | ||
201 | static void seccomp_debug(void) { | ||
202 | if (arg_debug == 0) | ||
203 | return; | ||
204 | |||
205 | pid_t child = fork(); | ||
206 | if (child < 0) | ||
207 | errExit("fork"); | ||
208 | if (child == 0) { | ||
209 | // dropping privs before calling system(3) | ||
210 | drop_privs(1); | ||
211 | printf("Seccomp directory:\n"); | ||
212 | int rv = system("ls -l " RUN_SECCOMP_DIR); | ||
213 | (void) rv; | ||
214 | printf("Active seccomp files:\n"); | ||
215 | rv = system("cat " RUN_SECCOMP_LIST); | ||
216 | (void) rv; | ||
217 | #ifdef HAVE_GCOV | ||
218 | __gcov_flush(); | ||
219 | #endif | ||
220 | _exit(0); | ||
221 | } | ||
222 | waitpid(child, NULL, 0); | ||
223 | } | ||
224 | #endif | ||
225 | |||
226 | static void sandbox_if_up(Bridge *br) { | 214 | static void sandbox_if_up(Bridge *br) { |
227 | assert(br); | 215 | assert(br); |
228 | if (!br->configured) | 216 | if (!br->configured) |
diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 73c9a6a8b..2390706f2 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c | |||
@@ -47,6 +47,9 @@ static char *usage_str = | |||
47 | " --caps.drop=capability,capability - blacklist capabilities filter.\n" | 47 | " --caps.drop=capability,capability - blacklist capabilities filter.\n" |
48 | " --caps.keep=capability,capability - whitelist capabilities filter.\n" | 48 | " --caps.keep=capability,capability - whitelist capabilities filter.\n" |
49 | " --caps.print=name|pid - print the caps filter.\n" | 49 | " --caps.print=name|pid - print the caps filter.\n" |
50 | #ifdef HAVE_FILE_TRANSFER | ||
51 | " --cat=name|pid filename - print content of file from sandbox container.\n" | ||
52 | #endif | ||
50 | " --cgroup=tasks-file - place the sandbox in the specified control group.\n" | 53 | " --cgroup=tasks-file - place the sandbox in the specified control group.\n" |
51 | #ifdef HAVE_CHROOT | 54 | #ifdef HAVE_CHROOT |
52 | " --chroot=dirname - chroot into directory.\n" | 55 | " --chroot=dirname - chroot into directory.\n" |
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index e216531ae..3b7ba4e3d 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -273,6 +273,10 @@ $ firejail \-\-list | |||
273 | $ firejail \-\-caps.print=3272 | 273 | $ firejail \-\-caps.print=3272 |
274 | 274 | ||
275 | .TP | 275 | .TP |
276 | \fB\-\-cat=name|pid filename | ||
277 | Print content of file from sandbox container, see FILE TRANSFER section for more details. | ||
278 | |||
279 | .TP | ||
276 | \fB\-\-cgroup=tasks-file | 280 | \fB\-\-cgroup=tasks-file |
277 | Place the sandbox in the specified control group. tasks-file is the full path of cgroup tasks file. | 281 | Place the sandbox in the specified control group. tasks-file is the full path of cgroup tasks file. |
278 | .br | 282 | .br |
@@ -344,7 +348,7 @@ $ firejail --dbus-system=filter --dbus-system.log --dbus-log=dbus.txt | |||
344 | 348 | ||
345 | .TP | 349 | .TP |
346 | \fB\-\-dbus-system=filter|none | 350 | \fB\-\-dbus-system=filter|none |
347 | Set system DBus sandboxing policy. | 351 | Set system DBus sandboxing policy. |
348 | .br | 352 | .br |
349 | 353 | ||
350 | .br | 354 | .br |
@@ -3031,6 +3035,12 @@ These features allow the user to inspect the filesystem container of an existing | |||
3031 | and transfer files between the container and the host filesystem. | 3035 | and transfer files between the container and the host filesystem. |
3032 | 3036 | ||
3033 | .TP | 3037 | .TP |
3038 | \fB\-\-cat=name|pid filename | ||
3039 | Write content of a container file to standard out. The container is specified by name or PID. | ||
3040 | If standard out is a terminal, all ASCII control characters except new line and horizontal tab | ||
3041 | are replaced. | ||
3042 | |||
3043 | .TP | ||
3034 | \fB\-\-get=name|pid filename | 3044 | \fB\-\-get=name|pid filename |
3035 | Retrieve the container file and store it on the host in the current working directory. | 3045 | Retrieve the container file and store it on the host in the current working directory. |
3036 | The container is specified by name or PID. | 3046 | The container is specified by name or PID. |
@@ -3074,6 +3084,10 @@ $ firejail \-\-get=mybrowser ~/Downloads/xpra-clipboard.png | |||
3074 | $ firejail \-\-put=mybrowser xpra-clipboard.png ~/Downloads/xpra-clipboard.png | 3084 | $ firejail \-\-put=mybrowser xpra-clipboard.png ~/Downloads/xpra-clipboard.png |
3075 | .br | 3085 | .br |
3076 | 3086 | ||
3087 | .br | ||
3088 | $ firejail \-\-cat=mybrowser ~/.bashrc | ||
3089 | .br | ||
3090 | |||
3077 | .SH MONITORING | 3091 | .SH MONITORING |
3078 | Option \-\-list prints a list of all sandboxes. The format | 3092 | Option \-\-list prints a list of all sandboxes. The format |
3079 | for each process entry is as follows: | 3093 | for each process entry is as follows: |
@@ -3261,7 +3275,7 @@ Homepage: https://firejail.wordpress.com | |||
3261 | \&\flfirejail-profile\fR\|(5), | 3275 | \&\flfirejail-profile\fR\|(5), |
3262 | \&\flfirejail-login\fR\|(5), | 3276 | \&\flfirejail-login\fR\|(5), |
3263 | \&\flfirejail-users\fR\|(5), | 3277 | \&\flfirejail-users\fR\|(5), |
3264 | .UR https://github.com/netblue30/firejail/wiki | 3278 | .UR https://github.com/netblue30/firejail/wiki |
3265 | .UE , | 3279 | .UE , |
3266 | .UR https://github.com/netblue30/firejail | 3280 | .UR https://github.com/netblue30/firejail |
3267 | .UE | 3281 | .UE |