aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/firejail/firejail.h3
-rw-r--r--src/firejail/sbox.c369
2 files changed, 198 insertions, 174 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index ea4012335..d35e0d155 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -830,10 +830,13 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc,
830#define SBOX_STDIN_FROM_FILE (1 << 6) // open file and redirect it to stdin 830#define SBOX_STDIN_FROM_FILE (1 << 6) // open file and redirect it to stdin
831#define SBOX_CAPS_HIDEPID (1 << 7) // hidepid caps filter for running firemon 831#define SBOX_CAPS_HIDEPID (1 << 7) // hidepid caps filter for running firemon
832#define SBOX_CAPS_NET_SERVICE (1 << 8) // caps filter for programs running network services 832#define SBOX_CAPS_NET_SERVICE (1 << 8) // caps filter for programs running network services
833#define SBOX_KEEP_FDS (1 << 9) // keep file descriptors open
834#define FIREJAIL_MAX_FD 20 // getdtablesize() is overkill for a firejail process
833 835
834// run sbox 836// run sbox
835int sbox_run(unsigned filter, int num, ...); 837int sbox_run(unsigned filter, int num, ...);
836int sbox_run_v(unsigned filter, char * const arg[]); 838int sbox_run_v(unsigned filter, char * const arg[]);
839void sbox_exec_v(unsigned filter, char * const arg[]);
837 840
838// run_files.c 841// run_files.c
839void delete_run_files(pid_t pid); 842void delete_run_files(pid_t pid);
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c
index e96b9cf79..e7fa267d8 100644
--- a/src/firejail/sbox.c
+++ b/src/firejail/sbox.c
@@ -23,7 +23,7 @@
23#include <unistd.h> 23#include <unistd.h>
24#include <net/if.h> 24#include <net/if.h>
25#include <stdarg.h> 25#include <stdarg.h>
26 #include <sys/wait.h> 26#include <sys/wait.h>
27#include "../include/seccomp.h" 27#include "../include/seccomp.h"
28 28
29#include <fcntl.h> 29#include <fcntl.h>
@@ -31,107 +31,230 @@
31#define O_PATH 010000000 31#define O_PATH 010000000
32#endif 32#endif
33 33
34int sbox_run(unsigned filtermask, int num, ...) { 34static int sbox_do_exec_v(unsigned filtermask, char * const arg[]) {
35 va_list valist; 35 int env_index = 0;
36 va_start(valist, num); 36 char *new_environment[256] = { NULL };
37 // preserve firejail-specific env vars
38 char *cl = getenv("FIREJAIL_FILE_COPY_LIMIT");
39 if (cl) {
40 if (asprintf(&new_environment[env_index++], "FIREJAIL_FILE_COPY_LIMIT=%s", cl) == -1)
41 errExit("asprintf");
42 }
43 clearenv();
44 if (arg_quiet) // --quiet is passed as an environment variable
45 new_environment[env_index++] = "FIREJAIL_QUIET=yes";
46 if (arg_debug) // --debug is passed as an environment variable
47 new_environment[env_index++] = "FIREJAIL_DEBUG=yes";
37 48
38 // build argument list 49 if (filtermask & SBOX_STDIN_FROM_FILE) {
39 char **arg = malloc((num + 1) * sizeof(char *)); 50 int fd;
40 int i; 51 if((fd = open(SBOX_STDIN_FILE, O_RDONLY)) == -1) {
41 for (i = 0; i < num; i++) 52 fprintf(stderr,"Error: cannot open %s\n", SBOX_STDIN_FILE);
42 arg[i] = va_arg(valist, char*); 53 exit(1);
43 arg[i] = NULL; 54 }
44 va_end(valist); 55 if (dup2(fd, STDIN_FILENO) == -1)
56 errExit("dup2");
57 close(fd);
58 }
59 else if ((filtermask & SBOX_ALLOW_STDIN) == 0) {
60 int fd = open("/dev/null",O_RDWR, 0);
61 if (fd != -1) {
62 if (dup2(fd, STDIN_FILENO) == -1)
63 errExit("dup2");
64 close(fd);
65 }
66 else // the user could run the sandbox without /dev/null
67 close(STDIN_FILENO);
68 }
45 69
46 int status = sbox_run_v(filtermask, arg); 70 // close all other file descriptors
71 if ((filtermask & SBOX_KEEP_FDS) == 0) {
72 int i;
73 for (i = 3; i < FIREJAIL_MAX_FD; i++)
74 close(i); // close open files
75 }
47 76
48 free(arg); 77 umask(027);
49 78
50 return status; 79 // apply filters
51} 80 if (filtermask & SBOX_CAPS_NONE) {
81 caps_drop_all();
82 } else {
83 uint64_t set = 0;
84 if (filtermask & SBOX_CAPS_NETWORK) {
85#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
86 set |= ((uint64_t) 1) << CAP_NET_ADMIN;
87 set |= ((uint64_t) 1) << CAP_NET_RAW;
88#endif
89 }
90 if (filtermask & SBOX_CAPS_HIDEPID) {
91#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
92 set |= ((uint64_t) 1) << CAP_SYS_PTRACE;
93 set |= ((uint64_t) 1) << CAP_SYS_PACCT;
94#endif
95 }
96 if (filtermask & SBOX_CAPS_NET_SERVICE) {
97#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
98 set |= ((uint64_t) 1) << CAP_NET_BIND_SERVICE;
99 set |= ((uint64_t) 1) << CAP_NET_BROADCAST;
100#endif
101 }
102 if (set != 0) { // some SBOX_CAPS_ flag was specified, drop all other capabilities
103#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
104 caps_set(set);
105#endif
106 }
107 }
52 108
53int sbox_run_v(unsigned filtermask, char * const arg[]) { 109 if (filtermask & SBOX_SECCOMP) {
54 struct sock_filter filter[] = { 110 struct sock_filter filter[] = {
55 VALIDATE_ARCHITECTURE, 111 VALIDATE_ARCHITECTURE,
56 EXAMINE_SYSCALL, 112 EXAMINE_SYSCALL,
57 113
58#if defined(__x86_64__) 114#if defined(__x86_64__)
59#define X32_SYSCALL_BIT 0x40000000 115#define X32_SYSCALL_BIT 0x40000000
60 // handle X32 ABI 116 // handle X32 ABI
61 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), 117 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, X32_SYSCALL_BIT, 1, 0),
62 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), 118 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, 0, 1, 0),
63 RETURN_ERRNO(EPERM), 119 RETURN_ERRNO(EPERM),
64#endif 120#endif
65 121
66 // syscall list 122 // syscall list
67#ifdef SYS_mount 123#ifdef SYS_mount
68 BLACKLIST(SYS_mount), // mount/unmount filesystems 124 BLACKLIST(SYS_mount), // mount/unmount filesystems
69#endif 125#endif
70#ifdef SYS_umount2 126#ifdef SYS_umount2
71 BLACKLIST(SYS_umount2), 127 BLACKLIST(SYS_umount2),
72#endif 128#endif
73#ifdef SYS_ptrace 129#ifdef SYS_ptrace
74 BLACKLIST(SYS_ptrace), // trace processes 130 BLACKLIST(SYS_ptrace), // trace processes
75#endif 131#endif
76#ifdef SYS_process_vm_readv 132#ifdef SYS_process_vm_readv
77 BLACKLIST(SYS_process_vm_readv), 133 BLACKLIST(SYS_process_vm_readv),
78#endif 134#endif
79#ifdef SYS_process_vm_writev 135#ifdef SYS_process_vm_writev
80 BLACKLIST(SYS_process_vm_writev), 136 BLACKLIST(SYS_process_vm_writev),
81#endif 137#endif
82#ifdef SYS_kexec_file_load 138#ifdef SYS_kexec_file_load
83 BLACKLIST(SYS_kexec_file_load), // loading a different kernel 139 BLACKLIST(SYS_kexec_file_load), // loading a different kernel
84#endif 140#endif
85#ifdef SYS_kexec_load 141#ifdef SYS_kexec_load
86 BLACKLIST(SYS_kexec_load), 142 BLACKLIST(SYS_kexec_load),
87#endif 143#endif
88#ifdef SYS_name_to_handle_at 144#ifdef SYS_name_to_handle_at
89 BLACKLIST(SYS_name_to_handle_at), 145 BLACKLIST(SYS_name_to_handle_at),
90#endif 146#endif
91#ifdef SYS_open_by_handle_at 147#ifdef SYS_open_by_handle_at
92 BLACKLIST(SYS_open_by_handle_at), // open by handle 148 BLACKLIST(SYS_open_by_handle_at), // open by handle
93#endif 149#endif
94#ifdef SYS_init_module 150#ifdef SYS_init_module
95 BLACKLIST(SYS_init_module), // kernel module handling 151 BLACKLIST(SYS_init_module), // kernel module handling
96#endif 152#endif
97#ifdef SYS_finit_module // introduced in 2013 153#ifdef SYS_finit_module // introduced in 2013
98 BLACKLIST(SYS_finit_module), 154 BLACKLIST(SYS_finit_module),
99#endif 155#endif
100#ifdef SYS_create_module 156#ifdef SYS_create_module
101 BLACKLIST(SYS_create_module), 157 BLACKLIST(SYS_create_module),
102#endif 158#endif
103#ifdef SYS_delete_module 159#ifdef SYS_delete_module
104 BLACKLIST(SYS_delete_module), 160 BLACKLIST(SYS_delete_module),
105#endif 161#endif
106#ifdef SYS_iopl 162#ifdef SYS_iopl
107 BLACKLIST(SYS_iopl), // io permissions 163 BLACKLIST(SYS_iopl), // io permissions
108#endif 164#endif
109#ifdef SYS_ioperm 165#ifdef SYS_ioperm
110 BLACKLIST(SYS_ioperm), 166 BLACKLIST(SYS_ioperm),
111#endif 167#endif
112#ifdef SYS_ioprio_set 168#ifdef SYS_ioprio_set
113 BLACKLIST(SYS_ioprio_set), 169 BLACKLIST(SYS_ioprio_set),
114#endif 170#endif
115#ifdef SYS_ni_syscall // new io permissions call on arm devices 171#ifdef SYS_ni_syscall // new io permissions call on arm devices
116 BLACKLIST(SYS_ni_syscall), 172 BLACKLIST(SYS_ni_syscall),
117#endif 173#endif
118#ifdef SYS_swapon 174#ifdef SYS_swapon
119 BLACKLIST(SYS_swapon), // swap on/off 175 BLACKLIST(SYS_swapon), // swap on/off
120#endif 176#endif
121#ifdef SYS_swapoff 177#ifdef SYS_swapoff
122 BLACKLIST(SYS_swapoff), 178 BLACKLIST(SYS_swapoff),
123#endif 179#endif
124#ifdef SYS_syslog 180#ifdef SYS_syslog
125 BLACKLIST(SYS_syslog), // kernel printk control 181 BLACKLIST(SYS_syslog), // kernel printk control
126#endif 182#endif
127 RETURN_ALLOW 183 RETURN_ALLOW
128 }; 184 };
185
186 struct sock_fprog prog = {
187 .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
188 .filter = filter,
189 };
190
191 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
192 perror("prctl(NO_NEW_PRIVS)");
193 }
194 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
195 perror("prctl(PR_SET_SECCOMP)");
196 }
197 }
198
199 if (filtermask & SBOX_ROOT) {
200 // elevate privileges in order to get grsecurity working
201 if (setreuid(0, 0))
202 errExit("setreuid");
203 if (setregid(0, 0))
204 errExit("setregid");
205 }
206 else if (filtermask & SBOX_USER)
207 drop_privs(1);
208
209 if (arg[0]) { // get rid of scan-build warning
210 int fd = open(arg[0], O_PATH | O_CLOEXEC);
211 if (fd == -1) {
212 if (errno == ENOENT) {
213 fprintf(stderr, "Error: %s does not exist\n", arg[0]);
214 exit(1);
215 } else {
216 errExit("open");
217 }
218 }
219 struct stat s;
220 if (fstat(fd, &s) == -1)
221 errExit("fstat");
222 if (s.st_uid != 0 && s.st_gid != 0) {
223 fprintf(stderr, "Error: %s is not owned by root, refusing to execute\n", arg[0]);
224 exit(1);
225 }
226 if (s.st_mode & 00002) {
227 fprintf(stderr, "Error: %s is world writable, refusing to execute\n", arg[0]);
228 exit(1);
229 }
230 fexecve(fd, arg, new_environment);
231 } else {
232 assert(0);
233 }
234 perror("fexecve");
235 _exit(1);
236}
237
238int sbox_run(unsigned filtermask, int num, ...) {
239 va_list valist;
240 va_start(valist, num);
241
242 // build argument list
243 char **arg = malloc((num + 1) * sizeof(char *));
244 int i;
245 for (i = 0; i < num; i++)
246 arg[i] = va_arg(valist, char *);
247 arg[i] = NULL;
248 va_end(valist);
249
250 int status = sbox_run_v(filtermask, arg);
251
252 free(arg);
129 253
130 struct sock_fprog prog = { 254 return status;
131 .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), 255}
132 .filter = filter,
133 };
134 256
257int sbox_run_v(unsigned filtermask, char * const arg[]) {
135 EUID_ROOT(); 258 EUID_ROOT();
136 259
137 if (arg_debug) { 260 if (arg_debug) {
@@ -144,132 +267,14 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
144 printf("\n"); 267 printf("\n");
145 } 268 }
146 269
270 // KEEP_FDS only makes sense with sbox_exec_v
271 assert((filtermask & SBOX_KEEP_FDS) == 0);
272
147 pid_t child = fork(); 273 pid_t child = fork();
148 if (child < 0) 274 if (child < 0)
149 errExit("fork"); 275 errExit("fork");
150 if (child == 0) { 276 if (child == 0) {
151 int env_index = 0; 277 sbox_do_exec_v(filtermask, arg);
152 char *new_environment[256] = { NULL };
153 // preserve firejail-specific env vars
154 char *cl = getenv("FIREJAIL_FILE_COPY_LIMIT");
155 if (cl) {
156 if (asprintf(&new_environment[env_index++], "FIREJAIL_FILE_COPY_LIMIT=%s", cl) == -1)
157 errExit("asprintf");
158 }
159 clearenv();
160 if (arg_quiet) // --quiet is passed as an environment variable
161 new_environment[env_index++] = "FIREJAIL_QUIET=yes";
162 if (arg_debug) // --debug is passed as an environment variable
163 new_environment[env_index++] = "FIREJAIL_DEBUG=yes";
164 if (cfg.seccomp_error_action)
165 if (asprintf(&new_environment[env_index++], "FIREJAIL_SECCOMP_ERROR_ACTION=%s", cfg.seccomp_error_action) == -1)
166 errExit("asprintf");
167
168 if (filtermask & SBOX_STDIN_FROM_FILE) {
169 int fd;
170 if((fd = open(SBOX_STDIN_FILE, O_RDONLY)) == -1) {
171 fprintf(stderr,"Error: cannot open %s\n", SBOX_STDIN_FILE);
172 exit(1);
173 }
174 if (dup2(fd, STDIN_FILENO) == -1)
175 errExit("dup2");
176 close(fd);
177 }
178 else if ((filtermask & SBOX_ALLOW_STDIN) == 0) {
179 int fd = open("/dev/null",O_RDWR, 0);
180 if (fd != -1) {
181 if (dup2(fd, STDIN_FILENO) == -1)
182 errExit("dup2");
183 close(fd);
184 }
185 else // the user could run the sandbox without /dev/null
186 close(STDIN_FILENO);
187 }
188
189 // close all other file descriptors
190 int max = 20; // getdtablesize() is overkill for a firejail process
191 int i = 3;
192 for (i = 3; i < max; i++)
193 close(i); // close open files
194
195 umask(027);
196
197 // apply filters
198 if (filtermask & SBOX_CAPS_NONE) {
199 caps_drop_all();
200 } else {
201 uint64_t set = 0;
202 if (filtermask & SBOX_CAPS_NETWORK) {
203#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
204 set |= ((uint64_t) 1) << CAP_NET_ADMIN;
205 set |= ((uint64_t) 1) << CAP_NET_RAW;
206#endif
207 }
208 if (filtermask & SBOX_CAPS_HIDEPID) {
209#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
210 set |= ((uint64_t) 1) << CAP_SYS_PTRACE;
211 set |= ((uint64_t) 1) << CAP_SYS_PACCT;
212#endif
213 }
214 if (filtermask & SBOX_CAPS_NET_SERVICE) {
215#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
216 set |= ((uint64_t) 1) << CAP_NET_BIND_SERVICE;
217 set |= ((uint64_t) 1) << CAP_NET_BROADCAST;
218#endif
219 }
220 if (set != 0) { // some SBOX_CAPS_ flag was specified, drop all other capabilities
221#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
222 caps_set(set);
223#endif
224 }
225 }
226
227 if (filtermask & SBOX_SECCOMP) {
228 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
229 perror("prctl(NO_NEW_PRIVS)");
230 }
231 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
232 perror("prctl(PR_SET_SECCOMP)");
233 }
234 }
235
236 if (filtermask & SBOX_ROOT) {
237 // elevate privileges in order to get grsecurity working
238 if (setreuid(0, 0))
239 errExit("setreuid");
240 if (setregid(0, 0))
241 errExit("setregid");
242 }
243 else if (filtermask & SBOX_USER)
244 drop_privs(1);
245
246 if (arg[0]) { // get rid of scan-build warning
247 int fd = open(arg[0], O_PATH | O_CLOEXEC);
248 if (fd == -1) {
249 if (errno == ENOENT) {
250 fprintf(stderr, "Error: %s does not exist\n", arg[0]);
251 exit(1);
252 } else {
253 errExit("open");
254 }
255 }
256 struct stat s;
257 if (fstat(fd, &s) == -1)
258 errExit("fstat");
259 if (s.st_uid != 0 && s.st_gid != 0) {
260 fprintf(stderr, "Error: %s is not owned by root, refusing to execute\n", arg[0]);
261 exit(1);
262 }
263 if (s.st_mode & 00002) {
264 fprintf(stderr, "Error: %s is world writable, refusing to execute\n", arg[0]);
265 exit(1);
266 }
267 fexecve(fd, arg, new_environment);
268 } else {
269 assert(0);
270 }
271 perror("fexecve");
272 _exit(1);
273 } 278 }
274 279
275 int status; 280 int status;
@@ -283,3 +288,19 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
283 288
284 return status; 289 return status;
285} 290}
291
292void sbox_exec_v(unsigned filtermask, char * const arg[]) {
293 EUID_ROOT();
294
295 if (arg_debug) {
296 printf("sbox exec: ");
297 int i = 0;
298 while (arg[i]) {
299 printf("%s ", arg[i]);
300 i++;
301 }
302 printf("\n");
303 }
304
305 sbox_do_exec_v(filtermask, arg);
306}