aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/caps.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/caps.c')
-rw-r--r--src/firejail/caps.c453
1 files changed, 453 insertions, 0 deletions
diff --git a/src/firejail/caps.c b/src/firejail/caps.c
new file mode 100644
index 000000000..d8777c0db
--- /dev/null
+++ b/src/firejail/caps.c
@@ -0,0 +1,453 @@
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
21#include "firejail.h"
22#include <errno.h>
23#include <linux/filter.h>
24#include <stddef.h>
25#include <linux/capability.h>
26#include <linux/audit.h>
27#include <sys/prctl.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <unistd.h>
31
32extern int capget(cap_user_header_t hdrp, cap_user_data_t datap);
33extern int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
34
35
36typedef struct {
37 char *name;
38 int nr;
39} CapsEntry;
40
41static CapsEntry capslist[] = {
42//
43// code generated using tools/extract-caps
44// updated manually based on kernel 3.18/include/linux/capability.h (added block suspend and audit_read
45//
46#ifdef CAP_CHOWN
47 {"chown", CAP_CHOWN },
48#endif
49#ifdef CAP_DAC_OVERRIDE
50 {"dac_override", CAP_DAC_OVERRIDE },
51#endif
52#ifdef CAP_DAC_READ_SEARCH
53 {"dac_read_search", CAP_DAC_READ_SEARCH },
54#endif
55#ifdef CAP_FOWNER
56 {"fowner", CAP_FOWNER },
57#endif
58#ifdef CAP_FSETID
59 {"fsetid", CAP_FSETID },
60#endif
61#ifdef CAP_KILL
62 {"kill", CAP_KILL },
63#endif
64#ifdef CAP_SETGID
65 {"setgid", CAP_SETGID },
66#endif
67#ifdef CAP_SETUID
68 {"setuid", CAP_SETUID },
69#endif
70#ifdef CAP_SETPCAP
71 {"setpcap", CAP_SETPCAP },
72#endif
73#ifdef CAP_LINUX_IMMUTABLE
74 {"linux_immutable", CAP_LINUX_IMMUTABLE },
75#endif
76#ifdef CAP_NET_BIND_SERVICE
77 {"net_bind_service", CAP_NET_BIND_SERVICE },
78#endif
79#ifdef CAP_NET_BROADCAST
80 {"net_broadcast", CAP_NET_BROADCAST },
81#endif
82#ifdef CAP_NET_ADMIN
83 {"net_admin", CAP_NET_ADMIN },
84#endif
85#ifdef CAP_NET_RAW
86 {"net_raw", CAP_NET_RAW },
87#endif
88#ifdef CAP_IPC_LOCK
89 {"ipc_lock", CAP_IPC_LOCK },
90#endif
91#ifdef CAP_IPC_OWNER
92 {"ipc_owner", CAP_IPC_OWNER },
93#endif
94#ifdef CAP_SYS_MODULE
95 {"sys_module", CAP_SYS_MODULE },
96#endif
97#ifdef CAP_SYS_RAWIO
98 {"sys_rawio", CAP_SYS_RAWIO },
99#endif
100#ifdef CAP_SYS_CHROOT
101 {"sys_chroot", CAP_SYS_CHROOT },
102#endif
103#ifdef CAP_SYS_PTRACE
104 {"sys_ptrace", CAP_SYS_PTRACE },
105#endif
106#ifdef CAP_SYS_PACCT
107 {"sys_pacct", CAP_SYS_PACCT },
108#endif
109#ifdef CAP_SYS_ADMIN
110 {"sys_admin", CAP_SYS_ADMIN },
111#endif
112#ifdef CAP_SYS_BOOT
113 {"sys_boot", CAP_SYS_BOOT },
114#endif
115#ifdef CAP_SYS_NICE
116 {"sys_nice", CAP_SYS_NICE },
117#endif
118#ifdef CAP_SYS_RESOURCE
119 {"sys_resource", CAP_SYS_RESOURCE },
120#endif
121#ifdef CAP_SYS_TIME
122 {"sys_time", CAP_SYS_TIME },
123#endif
124#ifdef CAP_SYS_TTY_CONFIG
125 {"sys_tty_config", CAP_SYS_TTY_CONFIG },
126#endif
127#ifdef CAP_MKNOD
128 {"mknod", CAP_MKNOD },
129#endif
130#ifdef CAP_LEASE
131 {"lease", CAP_LEASE },
132#endif
133#ifdef CAP_AUDIT_WRITE
134 {"audit_write", CAP_AUDIT_WRITE },
135#endif
136#ifdef CAP_AUDIT_CONTROL
137 {"audit_control", CAP_AUDIT_CONTROL },
138#endif
139#ifdef CAP_SETFCAP
140 {"setfcap", CAP_SETFCAP },
141#endif
142#ifdef CAP_MAC_OVERRIDE
143 {"mac_override", CAP_MAC_OVERRIDE },
144#endif
145#ifdef CAP_MAC_ADMIN
146 {"mac_admin", CAP_MAC_ADMIN },
147#endif
148#ifdef CAP_SYSLOG
149 {"syslog", CAP_SYSLOG },
150#endif
151#ifdef CAP_WAKE_ALARM
152 {"wake_alarm", CAP_WAKE_ALARM },
153#endif
154// not in Debian 7
155#ifdef CAP_BLOCK_SUSPEND
156 {"block_suspend", CAP_BLOCK_SUSPEND },
157#else
158 {"block_suspend", 36 },
159#endif
160#ifdef CAP_AUDIT_READ
161 {"audit_read", CAP_AUDIT_READ },
162#else
163 {"audit_read", 37 },
164#endif
165
166//
167// end of generated code
168//
169}; // end of capslist
170
171const char *caps_find_nr(int nr) {
172 int i;
173 int elems = sizeof(capslist) / sizeof(capslist[0]);
174 for (i = 0; i < elems; i++) {
175 if (nr == capslist[i].nr)
176 return capslist[i].name;
177 }
178
179 return "unknown";
180}
181
182// return -1 if error, or syscall number
183static int caps_find_name(const char *name) {
184 int i;
185 int elems = sizeof(capslist) / sizeof(capslist[0]);
186 for (i = 0; i < elems; i++) {
187 if (strcmp(name, capslist[i].name) == 0)
188 return capslist[i].nr;
189 }
190
191 return -1;
192}
193
194// return 1 if error, 0 if OK
195int caps_check_list(const char *clist, void (*callback)(int)) {
196 // don't allow empty lists
197 if (clist == NULL || *clist == '\0') {
198 fprintf(stderr, "Error: empty capabilities lists are not allowed\n");
199 return -1;
200 }
201
202 // work on a copy of the string
203 char *str = strdup(clist);
204 if (!str)
205 errExit("strdup");
206
207 char *ptr = str;
208 char *start = str;
209 while (*ptr != '\0') {
210 if (islower(*ptr) || isdigit(*ptr) || *ptr == '_')
211 ;
212 else if (*ptr == ',') {
213 *ptr = '\0';
214 int nr = caps_find_name(start);
215 if (nr == -1) {
216 fprintf(stderr, "Error: capability %s not found\n", start);
217 free(str);
218 return -1;
219 }
220 else if (callback != NULL)
221 callback(nr);
222
223 start = ptr + 1;
224 }
225 ptr++;
226 }
227 if (*start != '\0') {
228 int nr = caps_find_name(start);
229 if (nr == -1) {
230 fprintf(stderr, "Error: capability %s not found\n", start);
231 free(str);
232 return -1;
233 }
234 else if (callback != NULL)
235 callback(nr);
236 }
237
238 free(str);
239 return 0;
240}
241
242void caps_print(void) {
243 int i;
244 int elems = sizeof(capslist) / sizeof(capslist[0]);
245
246 // check current caps supported by the kernel
247 int cnt = 0;
248 unsigned long cap;
249 for (cap=0; cap <= 63; cap++) {
250 int code = prctl(PR_CAPBSET_DROP, cap, 0, 0, 0);
251 if (code == 0)
252 cnt++;
253 }
254 printf("Your kernel supports %d capabilities.\n", cnt);
255
256 for (i = 0; i < elems; i++) {
257 printf("%d\t- %s\n", capslist[i].nr, capslist[i].name);
258 }
259}
260
261
262
263
264// enabled by default
265int caps_default_filter(void) {
266 // drop capabilities
267 if (prctl(PR_CAPBSET_DROP, CAP_SYS_MODULE, 0, 0, 0) && arg_debug)
268 fprintf(stderr, "Warning: cannot drop CAP_SYS_MODULE");
269 else if (arg_debug)
270 printf("Drop CAP_SYS_MODULE\n");
271
272 if (prctl(PR_CAPBSET_DROP, CAP_SYS_RAWIO, 0, 0, 0) && arg_debug)
273 fprintf(stderr, "Warning: cannot drop CAP_SYS_RAWIO");
274 else if (arg_debug)
275 printf("Drop CAP_SYS_RAWIO\n");
276
277 if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0) && arg_debug)
278 fprintf(stderr, "Warning: cannot drop CAP_SYS_BOOT");
279 else if (arg_debug)
280 printf("Drop CAP_SYS_BOOT\n");
281
282 if (prctl(PR_CAPBSET_DROP, CAP_SYS_NICE, 0, 0, 0) && arg_debug)
283 fprintf(stderr, "Warning: cannot drop CAP_SYS_NICE");
284 else if (arg_debug)
285 printf("Drop CAP_SYS_NICE\n");
286
287 if (prctl(PR_CAPBSET_DROP, CAP_SYS_TTY_CONFIG, 0, 0, 0) && arg_debug)
288 fprintf(stderr, "Warning: cannot drop CAP_SYS_TTY_CONFIG");
289 else if (arg_debug)
290 printf("Drop CAP_SYS_TTY_CONFIG\n");
291
292 if (prctl(PR_CAPBSET_DROP, CAP_SYSLOG, 0, 0, 0) && arg_debug)
293 fprintf(stderr, "Warning: cannot drop CAP_SYSLOG");
294 else if (arg_debug)
295 printf("Drop CAP_SYSLOG\n");
296
297 if (prctl(PR_CAPBSET_DROP, CAP_MKNOD, 0, 0, 0) && arg_debug)
298 fprintf(stderr, "Warning: cannot drop CAP_MKNOD");
299 else if (arg_debug)
300 printf("Drop CAP_MKNOD\n");
301
302 if (prctl(PR_CAPBSET_DROP, CAP_SYS_ADMIN, 0, 0, 0) && arg_debug)
303 fprintf(stderr, "Warning: cannot drop CAP_SYS_ADMIN");
304 else if (arg_debug)
305 printf("Drop CAP_SYS_ADMIN\n");
306
307 return 0;
308}
309
310void caps_drop_all(void) {
311 if (arg_debug)
312 printf("Droping all capabilities\n");
313
314 unsigned long cap;
315 for (cap=0; cap <= 63; cap++) {
316 int code = prctl(PR_CAPBSET_DROP, cap, 0, 0, 0);
317 if (code == -1 && errno != EINVAL)
318 errExit("PR_CAPBSET_DROP");
319 }
320}
321
322
323void caps_set(uint64_t caps) {
324 if (arg_debug)
325 printf("Set caps filter %llx\n", (unsigned long long) caps);
326
327 unsigned long i;
328 uint64_t mask = 1LLU;
329 for (i = 0; i < 64; i++, mask <<= 1) {
330 if ((mask & caps) == 0) {
331 int code = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
332 if (code == -1 && errno != EINVAL)
333 errExit("PR_CAPBSET_DROP");
334 }
335 }
336}
337
338
339static uint64_t filter;
340
341static void caps_set_bit(int nr) {
342 uint64_t mask = 1LLU << nr;
343 filter |= mask;
344}
345static void caps_reset_bit(int nr) {
346 uint64_t mask = 1LLU << nr;
347 filter &= ~mask;
348}
349
350void caps_drop_list(const char *clist) {
351 filter = 0;
352 filter--;
353 caps_check_list(clist, caps_reset_bit);
354 caps_set(filter);
355}
356
357void caps_keep_list(const char *clist) {
358 filter = 0;
359 caps_check_list(clist, caps_set_bit);
360 caps_set(filter);
361}
362
363#define MAXBUF 4098
364static uint64_t extract_caps(int pid) {
365 char *file;
366 if (asprintf(&file, "/proc/%d/status", pid) == -1) {
367 errExit("asprintf");
368 exit(1);
369 }
370
371 FILE *fp = fopen(file, "r");
372 if (!fp) {
373 printf("Error: cannot open %s\n", file);
374 free(file);
375 exit(1);
376 }
377
378 char buf[MAXBUF];
379 while (fgets(buf, MAXBUF, fp)) {
380 if (strncmp(buf, "CapBnd:", 7) == 0) {
381 char *ptr = buf + 8;
382 unsigned long long val;
383 sscanf(ptr, "%llx", &val);
384 free(file);
385 fclose(fp);
386 return val;
387 }
388 }
389 fclose(fp);
390 free(file);
391 printf("Error: cannot read caps configuration\n");
392 exit(1);
393}
394
395
396void caps_print_filter_name(const char *name) {
397 if (!name || strlen(name) == 0) {
398 fprintf(stderr, "Error: invalid sandbox name\n");
399 exit(1);
400 }
401 pid_t pid;
402 if (name2pid(name, &pid)) {
403 fprintf(stderr, "Error: cannot find sandbox %s\n", name);
404 exit(1);
405 }
406
407 caps_print_filter(pid);
408}
409
410void caps_print_filter(pid_t pid) {
411 // if the pid is that of a firejail process, use the pid of the first child process
412 char *comm = pid_proc_comm(pid);
413 if (comm) {
414 // remove \n
415 char *ptr = strchr(comm, '\n');
416 if (ptr)
417 *ptr = '\0';
418 if (strcmp(comm, "firejail") == 0) {
419 pid_t child;
420 if (find_child(pid, &child) == 0) {
421 pid = child;
422 }
423 }
424 free(comm);
425 }
426
427 // check privileges for non-root users
428 uid_t uid = getuid();
429 if (uid != 0) {
430 struct stat s;
431 char *dir;
432 if (asprintf(&dir, "/proc/%u/ns", pid) == -1)
433 errExit("asprintf");
434 if (stat(dir, &s) < 0)
435 errExit("stat");
436 if (s.st_uid != uid) {
437 printf("Error: permission denied.\n");
438 exit(1);
439 }
440 }
441
442 uint64_t caps = extract_caps(pid);
443 drop_privs(1);
444
445 int i;
446 uint64_t mask;
447 int elems = sizeof(capslist) / sizeof(capslist[0]);
448 for (i = 0, mask = 1; i < elems; i++, mask <<= 1) {
449 printf("%-18.18s - %s\n", capslist[i].name, (mask & caps)? "enabled": "disabled");
450 }
451
452 exit(0);
453}