diff options
-rw-r--r-- | src/firejail/firejail.h | 3 | ||||
-rw-r--r-- | src/firejail/join.c | 65 | ||||
-rw-r--r-- | src/firejail/seccomp.c | 77 | ||||
-rwxr-xr-x | test/filters/seccomp-run-files.exp | 4 |
4 files changed, 110 insertions, 39 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 00c6cd8f3..fcaee18a2 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -55,7 +55,7 @@ | |||
55 | #define RUN_LIB_FILE "/run/firejail/mnt/libfiles" | 55 | #define RUN_LIB_FILE "/run/firejail/mnt/libfiles" |
56 | #define RUN_DNS_ETC "/run/firejail/mnt/dns-etc" | 56 | #define RUN_DNS_ETC "/run/firejail/mnt/dns-etc" |
57 | 57 | ||
58 | 58 | #define RUN_SECCOMP_LIST "/run/firejail/mnt/seccomp.list" // list of seccomp files installed | |
59 | #define RUN_SECCOMP_PROTOCOL "/run/firejail/mnt/seccomp.protocol" // protocol filter | 59 | #define RUN_SECCOMP_PROTOCOL "/run/firejail/mnt/seccomp.protocol" // protocol filter |
60 | #define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" // configured filter | 60 | #define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" // configured filter |
61 | #define RUN_SECCOMP_32 "/run/firejail/mnt/seccomp.32" // 32bit arch filter installed on 64bit architectures | 61 | #define RUN_SECCOMP_32 "/run/firejail/mnt/seccomp.32" // 32bit arch filter installed on 64bit architectures |
@@ -545,6 +545,7 @@ void fs_private_home_list(void); | |||
545 | 545 | ||
546 | 546 | ||
547 | // seccomp.c | 547 | // seccomp.c |
548 | void seccomp_load_file_list(void); | ||
548 | char *seccomp_check_list(const char *str); | 549 | char *seccomp_check_list(const char *str); |
549 | int seccomp_install_filters(void); | 550 | int seccomp_install_filters(void); |
550 | int seccomp_load(const char *fname); | 551 | int seccomp_load(const char *fname); |
diff --git a/src/firejail/join.c b/src/firejail/join.c index 731842275..28fd1b290 100644 --- a/src/firejail/join.c +++ b/src/firejail/join.c | |||
@@ -22,6 +22,8 @@ | |||
22 | #include <sys/wait.h> | 22 | #include <sys/wait.h> |
23 | #include <fcntl.h> | 23 | #include <fcntl.h> |
24 | #include <unistd.h> | 24 | #include <unistd.h> |
25 | #include <errno.h> | ||
26 | |||
25 | #include <sys/prctl.h> | 27 | #include <sys/prctl.h> |
26 | #include <errno.h> | 28 | #include <errno.h> |
27 | 29 | ||
@@ -71,8 +73,10 @@ static void extract_nogroups(pid_t pid) { | |||
71 | errExit("asprintf"); | 73 | errExit("asprintf"); |
72 | 74 | ||
73 | struct stat s; | 75 | struct stat s; |
74 | if (stat(fname, &s) == -1) | 76 | if (stat(fname, &s) == -1) { |
77 | free(fname); | ||
75 | return; | 78 | return; |
79 | } | ||
76 | 80 | ||
77 | arg_nogroups = 1; | 81 | arg_nogroups = 1; |
78 | free(fname); | 82 | free(fname); |
@@ -84,8 +88,10 @@ static void extract_cpu(pid_t pid) { | |||
84 | errExit("asprintf"); | 88 | errExit("asprintf"); |
85 | 89 | ||
86 | struct stat s; | 90 | struct stat s; |
87 | if (stat(fname, &s) == -1) | 91 | if (stat(fname, &s) == -1) { |
92 | free(fname); | ||
88 | return; | 93 | return; |
94 | } | ||
89 | 95 | ||
90 | // there is a CPU_CFG file, load it! | 96 | // there is a CPU_CFG file, load it! |
91 | load_cpu(fname); | 97 | load_cpu(fname); |
@@ -93,7 +99,7 @@ static void extract_cpu(pid_t pid) { | |||
93 | } | 99 | } |
94 | 100 | ||
95 | 101 | ||
96 | static void extract_caps_seccomp(pid_t pid) { | 102 | static void extract_caps(pid_t pid) { |
97 | // open stat file | 103 | // open stat file |
98 | char *file; | 104 | char *file; |
99 | if (asprintf(&file, "/proc/%u/status", pid) == -1) { | 105 | if (asprintf(&file, "/proc/%u/status", pid) == -1) { |
@@ -101,32 +107,35 @@ static void extract_caps_seccomp(pid_t pid) { | |||
101 | exit(1); | 107 | exit(1); |
102 | } | 108 | } |
103 | FILE *fp = fopen(file, "r"); | 109 | FILE *fp = fopen(file, "r"); |
104 | if (!fp) { | 110 | if (!fp) |
105 | free(file); | 111 | goto errexit; |
106 | fprintf(stderr, "Error: cannot open stat file for process %u\n", pid); | ||
107 | exit(1); | ||
108 | } | ||
109 | 112 | ||
110 | char buf[BUFLEN]; | 113 | char buf[BUFLEN]; |
111 | while (fgets(buf, BUFLEN - 1, fp)) { | 114 | while (fgets(buf, BUFLEN - 1, fp)) { |
112 | if (strncmp(buf, "Seccomp:", 8) == 0) { | 115 | if (strncmp(buf, "CapBnd:", 7) == 0) { |
113 | char *ptr = buf + 8; | ||
114 | int val; | ||
115 | sscanf(ptr, "%d", &val); | ||
116 | if (val == 2) | ||
117 | apply_seccomp = 1; | ||
118 | break; | ||
119 | } | ||
120 | else if (strncmp(buf, "CapBnd:", 7) == 0) { | ||
121 | char *ptr = buf + 7; | 116 | char *ptr = buf + 7; |
122 | unsigned long long val; | 117 | unsigned long long val; |
123 | sscanf(ptr, "%llx", &val); | 118 | if (sscanf(ptr, "%llx", &val) != 1) |
119 | goto errexit; | ||
124 | apply_caps = 1; | 120 | apply_caps = 1; |
125 | caps = val; | 121 | caps = val; |
126 | } | 122 | } |
123 | else if (strncmp(buf, "NoNewPrivs:", 11) == 0) { | ||
124 | char *ptr = buf + 11; | ||
125 | int val; | ||
126 | if (sscanf(ptr, "%d", &val) != 1) | ||
127 | goto errexit; | ||
128 | if (val) | ||
129 | arg_nonewprivs = 1; | ||
130 | } | ||
127 | } | 131 | } |
128 | fclose(fp); | 132 | fclose(fp); |
129 | free(file); | 133 | free(file); |
134 | return; | ||
135 | |||
136 | errexit: | ||
137 | fprintf(stderr, "Error: cannot read stat file for process %u\n", pid); | ||
138 | exit(1); | ||
130 | } | 139 | } |
131 | 140 | ||
132 | static void extract_user_namespace(pid_t pid) { | 141 | static void extract_user_namespace(pid_t pid) { |
@@ -187,7 +196,7 @@ pid_t switch_to_child(pid_t pid) { | |||
187 | char *comm = pid_proc_comm(pid); | 196 | char *comm = pid_proc_comm(pid); |
188 | if (!comm) { | 197 | if (!comm) { |
189 | if (errno == ENOENT) { | 198 | if (errno == ENOENT) { |
190 | fprintf(stderr, "Error: cannot find process with id %d\n", pid); | 199 | fprintf(stderr, "Error: cannot find process with pid %d\n", pid); |
191 | exit(1); | 200 | exit(1); |
192 | } | 201 | } |
193 | else { | 202 | else { |
@@ -240,7 +249,7 @@ void join(pid_t pid, int argc, char **argv, int index) { | |||
240 | EUID_ROOT(); | 249 | EUID_ROOT(); |
241 | // in user mode set caps seccomp, cpu, cgroup, etc | 250 | // in user mode set caps seccomp, cpu, cgroup, etc |
242 | if (getuid() != 0) { | 251 | if (getuid() != 0) { |
243 | extract_caps_seccomp(pid); | 252 | extract_caps(pid); |
244 | extract_cpu(pid); | 253 | extract_cpu(pid); |
245 | extract_nogroups(pid); | 254 | extract_nogroups(pid); |
246 | extract_user_namespace(pid); | 255 | extract_user_namespace(pid); |
@@ -309,15 +318,8 @@ void join(pid_t pid, int argc, char **argv, int index) { | |||
309 | if (apply_caps == 1) // not available for uid 0 | 318 | if (apply_caps == 1) // not available for uid 0 |
310 | caps_set(caps); | 319 | caps_set(caps); |
311 | #ifdef HAVE_SECCOMP | 320 | #ifdef HAVE_SECCOMP |
312 | // read cfg.protocol from file | ||
313 | if (getuid() != 0) | 321 | if (getuid() != 0) |
314 | protocol_filter_load(RUN_PROTOCOL_CFG); | 322 | seccomp_load_file_list(); |
315 | if (cfg.protocol) // not available for uid 0 | ||
316 | seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter | ||
317 | |||
318 | // set seccomp filter | ||
319 | if (apply_seccomp == 1) // not available for uid 0 | ||
320 | seccomp_load(RUN_SECCOMP_CFG); | ||
321 | #endif | 323 | #endif |
322 | 324 | ||
323 | // mount user namespace or drop privileges | 325 | // mount user namespace or drop privileges |
@@ -333,6 +335,13 @@ void join(pid_t pid, int argc, char **argv, int index) { | |||
333 | caps_set(caps); | 335 | caps_set(caps); |
334 | } | 336 | } |
335 | 337 | ||
338 | // set nonewprivs | ||
339 | if (arg_nonewprivs == 1) { // not available for uid 0 | ||
340 | int rv = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); | ||
341 | if (arg_debug && rv == 0) | ||
342 | printf("NO_NEW_PRIVS set\n"); | ||
343 | } | ||
344 | |||
336 | EUID_USER(); | 345 | EUID_USER(); |
337 | // set nice | 346 | // set nice |
338 | if (arg_nice) { | 347 | if (arg_nice) { |
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 7be7b3950..a5323d840 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c | |||
@@ -89,6 +89,41 @@ int seccomp_install_filters(void) { | |||
89 | return r; | 89 | return r; |
90 | } | 90 | } |
91 | 91 | ||
92 | static void seccomp_save_file_list(const char *fname) { | ||
93 | assert(fname); | ||
94 | |||
95 | FILE *fp = fopen(RUN_SECCOMP_LIST, "a+"); | ||
96 | if (!fp) | ||
97 | errExit("fopen"); | ||
98 | |||
99 | fprintf(fp, "%s\n", fname); | ||
100 | fclose(fp); | ||
101 | int rv = chown(RUN_SECCOMP_LIST, getuid(), getgid()); | ||
102 | (void) rv; | ||
103 | } | ||
104 | |||
105 | #define MAXBUF 4096 | ||
106 | static int load_file_list_flag = 0; | ||
107 | void seccomp_load_file_list(void) { | ||
108 | FILE *fp = fopen(RUN_SECCOMP_LIST, "r"); | ||
109 | if (!fp) | ||
110 | return; // no seccomp configuration whatsoever | ||
111 | |||
112 | load_file_list_flag = 1; | ||
113 | char buf[MAXBUF]; | ||
114 | while (fgets(buf, MAXBUF, fp)) { | ||
115 | // clean '\n' | ||
116 | char *ptr = strchr(buf, '\n'); | ||
117 | if (ptr) | ||
118 | *ptr = '\0'; | ||
119 | seccomp_load(buf); | ||
120 | } | ||
121 | |||
122 | fclose(fp); | ||
123 | load_file_list_flag = 0; | ||
124 | } | ||
125 | |||
126 | |||
92 | int seccomp_load(const char *fname) { | 127 | int seccomp_load(const char *fname) { |
93 | assert(fname); | 128 | assert(fname); |
94 | 129 | ||
@@ -131,6 +166,10 @@ int seccomp_load(const char *fname) { | |||
131 | PATH_FSEC_PRINT, fname); | 166 | PATH_FSEC_PRINT, fname); |
132 | } | 167 | } |
133 | 168 | ||
169 | // save the file name in seccomp list | ||
170 | if (!load_file_list_flag) | ||
171 | seccomp_save_file_list(fname); | ||
172 | |||
134 | return 0; | 173 | return 0; |
135 | errexit: | 174 | errexit: |
136 | fprintf(stderr, "Error: cannot read %s\n", fname); | 175 | fprintf(stderr, "Error: cannot read %s\n", fname); |
@@ -314,23 +353,45 @@ void seccomp_print_filter(pid_t pid) { | |||
314 | } | 353 | } |
315 | } | 354 | } |
316 | 355 | ||
317 | // find the seccomp filter | 356 | // find the seccomp list file |
318 | EUID_ROOT(); | 357 | EUID_ROOT(); |
319 | char *fname; | 358 | char *fname; |
320 | if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_SECCOMP_CFG) == -1) | 359 | if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_SECCOMP_LIST) == -1) |
321 | errExit("asprintf"); | 360 | errExit("asprintf"); |
322 | 361 | ||
323 | struct stat s; | 362 | struct stat s; |
324 | if (stat(fname, &s) == -1) { | 363 | if (stat(fname, &s) == -1) |
325 | printf("Cannot access seccomp filter.\n"); | 364 | goto errexit; |
326 | exit(1); | ||
327 | } | ||
328 | 365 | ||
329 | // read and print the filter - run this as root, the user doesn't have access | 366 | FILE *fp = fopen(fname, "r"); |
330 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 2, PATH_FSEC_PRINT, fname); | 367 | if (!fp) |
368 | goto errexit; | ||
331 | free(fname); | 369 | free(fname); |
332 | 370 | ||
371 | char buf[MAXBUF]; | ||
372 | while (fgets(buf, MAXBUF, fp)) { | ||
373 | // clean '\n' | ||
374 | char *ptr = strchr(buf, '\n'); | ||
375 | if (ptr) | ||
376 | *ptr = '\0'; | ||
377 | |||
378 | if (asprintf(&fname, "/proc/%d/root%s", pid, buf) == -1) | ||
379 | errExit("asprintf"); | ||
380 | printf("FILE: %s\n", fname); fflush(0); | ||
381 | |||
382 | // read and print the filter - run this as root, the user doesn't have access | ||
383 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 2, PATH_FSEC_PRINT, fname); | ||
384 | fflush(0); | ||
385 | |||
386 | printf("\n"); fflush(0); | ||
387 | free(fname); | ||
388 | } | ||
389 | fclose(fp); | ||
333 | exit(0); | 390 | exit(0); |
391 | |||
392 | errexit: | ||
393 | printf("Cannot access seccomp filter.\n"); | ||
394 | exit(1); | ||
334 | } | 395 | } |
335 | 396 | ||
336 | #endif // HAVE_SECCOMP | 397 | #endif // HAVE_SECCOMP |
diff --git a/test/filters/seccomp-run-files.exp b/test/filters/seccomp-run-files.exp index 7c436b664..7a1345902 100755 --- a/test/filters/seccomp-run-files.exp +++ b/test/filters/seccomp-run-files.exp | |||
@@ -24,7 +24,7 @@ after 100 | |||
24 | send -- "ls -l /run/firejail/mnt | grep -c seccomp\r" | 24 | send -- "ls -l /run/firejail/mnt | grep -c seccomp\r" |
25 | expect { | 25 | expect { |
26 | timeout {puts "TESTING ERROR 3\n";exit} | 26 | timeout {puts "TESTING ERROR 3\n";exit} |
27 | "4" | 27 | "5" |
28 | } | 28 | } |
29 | send -- "exit\r" | 29 | send -- "exit\r" |
30 | sleep 1 | 30 | sleep 1 |
@@ -90,7 +90,7 @@ after 100 | |||
90 | send -- "ls -l /run/firejail/mnt | grep -c seccomp\r" | 90 | send -- "ls -l /run/firejail/mnt | grep -c seccomp\r" |
91 | expect { | 91 | expect { |
92 | timeout {puts "TESTING ERROR 18\n";exit} | 92 | timeout {puts "TESTING ERROR 18\n";exit} |
93 | "5" | 93 | "6" |
94 | } | 94 | } |
95 | send -- "exit\r" | 95 | send -- "exit\r" |
96 | sleep 1 | 96 | sleep 1 |