diff options
Diffstat (limited to 'src')
120 files changed, 10027 insertions, 4704 deletions
diff --git a/src/bash_completion/firejail.bash_completion b/src/bash_completion/firejail.bash_completion index 21e28c98b..d3dcd57d0 100644 --- a/src/bash_completion/firejail.bash_completion +++ b/src/bash_completion/firejail.bash_completion | |||
@@ -47,6 +47,10 @@ _firejail() | |||
47 | _filedir | 47 | _filedir |
48 | return 0 | 48 | return 0 |
49 | ;; | 49 | ;; |
50 | --read-write) | ||
51 | _filedir | ||
52 | return 0 | ||
53 | ;; | ||
50 | --bind) | 54 | --bind) |
51 | _filedir | 55 | _filedir |
52 | return 0 | 56 | return 0 |
@@ -63,6 +67,10 @@ _firejail() | |||
63 | _filedir | 67 | _filedir |
64 | return 0 | 68 | return 0 |
65 | ;; | 69 | ;; |
70 | --audit) | ||
71 | _filedir | ||
72 | return 0 | ||
73 | ;; | ||
66 | --net) | 74 | --net) |
67 | comps=$(__interfaces) | 75 | comps=$(__interfaces) |
68 | COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) | 76 | COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) |
diff --git a/src/faudit/Makefile.in b/src/faudit/Makefile.in new file mode 100644 index 000000000..995a0bf49 --- /dev/null +++ b/src/faudit/Makefile.in | |||
@@ -0,0 +1,25 @@ | |||
1 | all: faudit | ||
2 | |||
3 | PREFIX=@prefix@ | ||
4 | VERSION=@PACKAGE_VERSION@ | ||
5 | NAME=@PACKAGE_NAME@ | ||
6 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ | ||
7 | |||
8 | H_FILE_LIST = $(sort $(wildcard *.[h])) | ||
9 | C_FILE_LIST = $(sort $(wildcard *.c)) | ||
10 | OBJS = $(C_FILE_LIST:.c=.o) | ||
11 | BINOBJS = $(foreach file, $(OBJS), $file) | ||
12 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(PREFIX)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | ||
13 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | ||
14 | |||
15 | %.o : %.c $(H_FILE_LIST) | ||
16 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | ||
17 | |||
18 | faudit: $(OBJS) | ||
19 | $(CC) $(LDFLAGS) -o $@ $(OBJS) | ||
20 | |||
21 | clean:; rm -f *.o faudit | ||
22 | |||
23 | distclean: clean | ||
24 | rm -fr Makefile | ||
25 | |||
diff --git a/src/faudit/caps.c b/src/faudit/caps.c new file mode 100644 index 000000000..d4a62b34f --- /dev/null +++ b/src/faudit/caps.c | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | |||
21 | #include "faudit.h" | ||
22 | #include <linux/capability.h> | ||
23 | |||
24 | #define MAXBUF 4098 | ||
25 | static int extract_caps(uint64_t *val) { | ||
26 | FILE *fp = fopen("/proc/self/status", "r"); | ||
27 | if (!fp) | ||
28 | return 1; | ||
29 | |||
30 | char buf[MAXBUF]; | ||
31 | while (fgets(buf, MAXBUF, fp)) { | ||
32 | if (strncmp(buf, "CapBnd:\t", 8) == 0) { | ||
33 | char *ptr = buf + 8; | ||
34 | unsigned long long tmp; | ||
35 | sscanf(ptr, "%llx", &tmp); | ||
36 | *val = tmp; | ||
37 | fclose(fp); | ||
38 | return 0; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | fclose(fp); | ||
43 | return 1; | ||
44 | } | ||
45 | |||
46 | // return 1 if the capability is in tbe map | ||
47 | static int check_capability(uint64_t map, int cap) { | ||
48 | int i; | ||
49 | uint64_t mask = 1ULL; | ||
50 | |||
51 | for (i = 0; i < 64; i++, mask <<= 1) { | ||
52 | if ((i == cap) && (mask & map)) | ||
53 | return 1; | ||
54 | } | ||
55 | |||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | void caps_test(void) { | ||
60 | uint64_t caps_val; | ||
61 | |||
62 | if (extract_caps(&caps_val)) { | ||
63 | printf("SKIP: cannot extract capabilities on this platform.\n"); | ||
64 | return; | ||
65 | } | ||
66 | |||
67 | if (caps_val) { | ||
68 | printf("BAD: the capability map is %llx, it should be all zero. ", (unsigned long long) caps_val); | ||
69 | printf("Use \"firejail --caps.drop=all\" to fix it.\n"); | ||
70 | |||
71 | if (check_capability(caps_val, CAP_SYS_ADMIN)) | ||
72 | printf("UGLY: CAP_SYS_ADMIN is enabled.\n"); | ||
73 | if (check_capability(caps_val, CAP_SYS_BOOT)) | ||
74 | printf("UGLY: CAP_SYS_BOOT is enabled.\n"); | ||
75 | } | ||
76 | else | ||
77 | printf("GOOD: all capabilities are disabled.\n"); | ||
78 | } | ||
79 | |||
diff --git a/src/faudit/dbus.c b/src/faudit/dbus.c new file mode 100644 index 000000000..4debf2ff6 --- /dev/null +++ b/src/faudit/dbus.c | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "faudit.h" | ||
21 | #include <sys/socket.h> | ||
22 | #include <sys/un.h> | ||
23 | |||
24 | // return 0 if the connection is possible | ||
25 | int check_unix(const char *sockfile) { | ||
26 | assert(sockfile); | ||
27 | int rv = -1; | ||
28 | |||
29 | // open socket | ||
30 | int sock = socket(AF_UNIX, SOCK_STREAM, 0); | ||
31 | if (sock == -1) | ||
32 | return rv; | ||
33 | |||
34 | // connect | ||
35 | struct sockaddr_un remote; | ||
36 | memset(&remote, 0, sizeof(struct sockaddr_un)); | ||
37 | remote.sun_family = AF_UNIX; | ||
38 | strcpy(remote.sun_path, sockfile); | ||
39 | int len = strlen(remote.sun_path) + sizeof(remote.sun_family); | ||
40 | if (*sockfile == '@') | ||
41 | remote.sun_path[0] = '\0'; | ||
42 | if (connect(sock, (struct sockaddr *)&remote, len) == 0) | ||
43 | rv = 0; | ||
44 | |||
45 | close(sock); | ||
46 | return rv; | ||
47 | } | ||
48 | |||
49 | void dbus_test(void) { | ||
50 | // check the session bus | ||
51 | char *str = getenv("DBUS_SESSION_BUS_ADDRESS"); | ||
52 | if (str) { | ||
53 | int rv = 0; | ||
54 | char *bus = strdup(str); | ||
55 | if (!bus) | ||
56 | errExit("strdup"); | ||
57 | char *sockfile; | ||
58 | if ((sockfile = strstr(bus, "unix:abstract=")) != NULL) { | ||
59 | sockfile += 13; | ||
60 | *sockfile = '@'; | ||
61 | char *ptr = strchr(sockfile, ','); | ||
62 | if (ptr) | ||
63 | *ptr = '\0'; | ||
64 | rv = check_unix(sockfile); | ||
65 | *sockfile = '@'; | ||
66 | if (rv == 0) | ||
67 | printf("MAYBE: D-Bus socket %s is available\n", sockfile); | ||
68 | else if (rv == -1) | ||
69 | printf("GOOD: cannot connect to D-Bus socket %s\n", sockfile); | ||
70 | } | ||
71 | else if ((sockfile = strstr(bus, "unix:path=")) != NULL) { | ||
72 | sockfile += 10; | ||
73 | char *ptr = strchr(sockfile, ','); | ||
74 | if (ptr) | ||
75 | *ptr = '\0'; | ||
76 | rv = check_unix(sockfile); | ||
77 | if (rv == 0) | ||
78 | printf("MAYBE: D-Bus socket %s is available\n", sockfile); | ||
79 | else if (rv == -1) | ||
80 | printf("GOOD: cannot connect to D-Bus socket %s\n", sockfile); | ||
81 | } | ||
82 | else if ((sockfile = strstr(bus, "tcp:host=")) != NULL) | ||
83 | printf("UGLY: session bus configured for TCP communication.\n"); | ||
84 | else | ||
85 | printf("GOOD: cannot find a D-Bus socket\n"); | ||
86 | |||
87 | |||
88 | free(bus); | ||
89 | } | ||
90 | else | ||
91 | printf("GOOD: DBUS_SESSION_BUS_ADDRESS environment variable not configured."); | ||
92 | } | ||
93 | |||
94 | |||
95 | |||
diff --git a/src/faudit/dev.c b/src/faudit/dev.c new file mode 100644 index 000000000..92f615958 --- /dev/null +++ b/src/faudit/dev.c | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "faudit.h" | ||
21 | #include <dirent.h> | ||
22 | |||
23 | void dev_test(void) { | ||
24 | DIR *dir; | ||
25 | if (!(dir = opendir("/dev"))) { | ||
26 | fprintf(stderr, "Error: cannot open /dev directory\n"); | ||
27 | return; | ||
28 | } | ||
29 | |||
30 | struct dirent *entry; | ||
31 | printf("INFO: files visible in /dev directory: "); | ||
32 | int cnt = 0; | ||
33 | while ((entry = readdir(dir)) != NULL) { | ||
34 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) | ||
35 | continue; | ||
36 | |||
37 | printf("%s, ", entry->d_name); | ||
38 | cnt++; | ||
39 | } | ||
40 | printf("\n"); | ||
41 | |||
42 | if (cnt > 20) | ||
43 | printf("MAYBE: /dev directory seems to be fully populated. Use --private-dev or --whitelist to restrict the access.\n"); | ||
44 | else | ||
45 | printf("GOOD: Access to /dev directory is restricted.\n"); | ||
46 | closedir(dir); | ||
47 | } | ||
diff --git a/src/faudit/faudit.h b/src/faudit/faudit.h new file mode 100644 index 000000000..17c754c3b --- /dev/null +++ b/src/faudit/faudit.h | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | |||
21 | #ifndef FAUDIT_H | ||
22 | #define FAUDIT_H | ||
23 | #define _GNU_SOURCE | ||
24 | #include <stdio.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <stdint.h> | ||
27 | #include <string.h> | ||
28 | #include <unistd.h> | ||
29 | #include <sys/types.h> | ||
30 | #include <sys/stat.h> | ||
31 | #include <sys/mount.h> | ||
32 | #include <assert.h> | ||
33 | |||
34 | #define errExit(msg) do { char msgout[500]; sprintf(msgout, "Error %s:%s(%d)", msg, __FUNCTION__, __LINE__); perror(msgout); exit(1);} while (0) | ||
35 | |||
36 | // main.c | ||
37 | extern char *prog; | ||
38 | |||
39 | // pid.c | ||
40 | void pid_test(void); | ||
41 | |||
42 | // caps.c | ||
43 | void caps_test(void); | ||
44 | |||
45 | // seccomp.c | ||
46 | void seccomp_test(void); | ||
47 | |||
48 | // syscall.c | ||
49 | void syscall_helper(int argc, char **argv); | ||
50 | void syscall_run(const char *name); | ||
51 | |||
52 | // files.c | ||
53 | void files_test(void); | ||
54 | |||
55 | // network.c | ||
56 | void network_test(void); | ||
57 | |||
58 | // dbus.c | ||
59 | int check_unix(const char *sockfile); | ||
60 | void dbus_test(void); | ||
61 | |||
62 | // dev.c | ||
63 | void dev_test(void); | ||
64 | |||
65 | // x11.c | ||
66 | void x11_test(void); | ||
67 | |||
68 | #endif | ||
diff --git a/src/faudit/files.c b/src/faudit/files.c new file mode 100644 index 000000000..67b43f22b --- /dev/null +++ b/src/faudit/files.c | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "faudit.h" | ||
21 | #include <fcntl.h> | ||
22 | #include <pwd.h> | ||
23 | |||
24 | static char *username = NULL; | ||
25 | static char *homedir = NULL; | ||
26 | |||
27 | static void check_home_file(const char *name) { | ||
28 | assert(homedir); | ||
29 | |||
30 | char *fname; | ||
31 | if (asprintf(&fname, "%s/%s", homedir, name) == -1) | ||
32 | errExit("asprintf"); | ||
33 | |||
34 | if (access(fname, R_OK) == 0) { | ||
35 | printf("UGLY: I can access files in %s directory. ", fname); | ||
36 | printf("Use \"firejail --blacklist=%s\" to block it.\n", fname); | ||
37 | } | ||
38 | else | ||
39 | printf("GOOD: I cannot access files in %s directory.\n", fname); | ||
40 | |||
41 | free(fname); | ||
42 | } | ||
43 | |||
44 | void files_test(void) { | ||
45 | struct passwd *pw = getpwuid(getuid()); | ||
46 | if (!pw) { | ||
47 | fprintf(stderr, "Error: cannot retrieve user account information\n"); | ||
48 | return; | ||
49 | } | ||
50 | |||
51 | username = strdup(pw->pw_name); | ||
52 | if (!username) | ||
53 | errExit("strdup"); | ||
54 | homedir = strdup(pw->pw_dir); | ||
55 | if (!homedir) | ||
56 | errExit("strdup"); | ||
57 | |||
58 | // check access to .ssh directory | ||
59 | check_home_file(".ssh"); | ||
60 | |||
61 | // check access to .gnupg directory | ||
62 | check_home_file(".gnupg"); | ||
63 | |||
64 | // check access to Firefox browser directory | ||
65 | check_home_file(".mozilla"); | ||
66 | |||
67 | // check access to Chromium browser directory | ||
68 | check_home_file(".config/chromium"); | ||
69 | |||
70 | // check access to Debian Icedove directory | ||
71 | check_home_file(".icedove"); | ||
72 | |||
73 | // check access to Thunderbird directory | ||
74 | check_home_file(".thunderbird"); | ||
75 | } | ||
diff --git a/src/faudit/main.c b/src/faudit/main.c new file mode 100644 index 000000000..7f47ccaf0 --- /dev/null +++ b/src/faudit/main.c | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "faudit.h" | ||
21 | char *prog; | ||
22 | |||
23 | int main(int argc, char **argv) { | ||
24 | // make test-arguments helper | ||
25 | if (getenv("FIREJAIL_TEST_ARGUMENTS")) { | ||
26 | printf("Arguments:\n"); | ||
27 | |||
28 | int i; | ||
29 | for (i = 0; i < argc; i++) { | ||
30 | printf("#%s#\n", argv[i]); | ||
31 | } | ||
32 | |||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | |||
37 | if (argc != 1) { | ||
38 | int i; | ||
39 | |||
40 | for (i = 1; i < argc; i++) { | ||
41 | if (strcmp(argv[i], "syscall")) { | ||
42 | syscall_helper(argc, argv); | ||
43 | return 0; | ||
44 | } | ||
45 | } | ||
46 | return 1; | ||
47 | } | ||
48 | |||
49 | printf("\n---------------- Firejail Audit: the GOOD, the BAD and the UGLY ----------------\n"); | ||
50 | |||
51 | // extract program name | ||
52 | prog = realpath(argv[0], NULL); | ||
53 | if (prog == NULL) { | ||
54 | prog = strdup("faudit"); | ||
55 | if (!prog) | ||
56 | errExit("strdup"); | ||
57 | } | ||
58 | printf("INFO: starting %s.\n", prog); | ||
59 | |||
60 | |||
61 | // check pid namespace | ||
62 | pid_test(); | ||
63 | printf("\n"); | ||
64 | |||
65 | // check seccomp | ||
66 | seccomp_test(); | ||
67 | printf("\n"); | ||
68 | |||
69 | // check capabilities | ||
70 | caps_test(); | ||
71 | printf("\n"); | ||
72 | |||
73 | // check some well-known problematic files and directories | ||
74 | files_test(); | ||
75 | printf("\n"); | ||
76 | |||
77 | // network | ||
78 | network_test(); | ||
79 | printf("\n"); | ||
80 | |||
81 | // dbus | ||
82 | dbus_test(); | ||
83 | printf("\n"); | ||
84 | |||
85 | // x11 test | ||
86 | x11_test(); | ||
87 | printf("\n"); | ||
88 | |||
89 | // /dev test | ||
90 | dev_test(); | ||
91 | printf("\n"); | ||
92 | |||
93 | |||
94 | free(prog); | ||
95 | printf("--------------------------------------------------------------------------------\n"); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
diff --git a/src/faudit/network.c b/src/faudit/network.c new file mode 100644 index 000000000..cf1eede69 --- /dev/null +++ b/src/faudit/network.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "faudit.h" | ||
21 | #include <sys/socket.h> | ||
22 | #include <arpa/inet.h> | ||
23 | #include <linux/netlink.h> | ||
24 | #include <linux/rtnetlink.h> | ||
25 | |||
26 | static void check_ssh(void) { | ||
27 | // open socket | ||
28 | int sock = socket(AF_INET, SOCK_STREAM, 0); | ||
29 | if (sock == -1) { | ||
30 | printf("GOOD: SSH server not available on localhost.\n"); | ||
31 | return; | ||
32 | } | ||
33 | |||
34 | // connect to localhost | ||
35 | struct sockaddr_in server; | ||
36 | server.sin_addr.s_addr = inet_addr("127.0.0.1"); | ||
37 | server.sin_family = AF_INET; | ||
38 | server.sin_port = htons(22); | ||
39 | |||
40 | if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0) | ||
41 | printf("GOOD: SSH server not available on localhost.\n"); | ||
42 | else { | ||
43 | printf("MAYBE: an SSH server is accessible on localhost. "); | ||
44 | printf("It could be a good idea to create a new network namespace using \"--net=none\" or \"--net=eth0\".\n"); | ||
45 | } | ||
46 | |||
47 | close(sock); | ||
48 | } | ||
49 | |||
50 | static void check_http(void) { | ||
51 | // open socket | ||
52 | int sock = socket(AF_INET, SOCK_STREAM, 0); | ||
53 | if (sock == -1) { | ||
54 | printf("GOOD: HTTP server not available on localhost.\n"); | ||
55 | return; | ||
56 | } | ||
57 | |||
58 | // connect to localhost | ||
59 | struct sockaddr_in server; | ||
60 | server.sin_addr.s_addr = inet_addr("127.0.0.1"); | ||
61 | server.sin_family = AF_INET; | ||
62 | server.sin_port = htons(80); | ||
63 | |||
64 | if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0) | ||
65 | printf("GOOD: HTTP server not available on localhost.\n"); | ||
66 | else { | ||
67 | printf("MAYBE: an HTTP server is accessible on localhost. "); | ||
68 | printf("It could be a good idea to create a new network namespace using \"--net=none\" or \"--net=eth0\".\n"); | ||
69 | } | ||
70 | |||
71 | close(sock); | ||
72 | } | ||
73 | |||
74 | void check_netlink(void) { | ||
75 | int sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, 0); | ||
76 | if (sock == -1) { | ||
77 | printf("GOOD: I cannot connect to netlink socket. Network utilities such as iproute2 will not work in the sandbox.\n"); | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | struct sockaddr_nl local; | ||
82 | memset(&local, 0, sizeof(local)); | ||
83 | local.nl_family = AF_NETLINK; | ||
84 | local.nl_groups = 0; //subscriptions; | ||
85 | |||
86 | if (bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0) { | ||
87 | printf("GOOD: I cannot connect to netlink socket. Network utilities such as iproute2 will not work in the sandbox.\n"); | ||
88 | close(sock); | ||
89 | return; | ||
90 | } | ||
91 | |||
92 | close(sock); | ||
93 | printf("MAYBE: I can connect to netlink socket. Network utilities such as iproute2 will work fine in the sandbox. "); | ||
94 | printf("You can use \"--protocol\" to disable the socket.\n"); | ||
95 | } | ||
96 | |||
97 | void network_test(void) { | ||
98 | check_ssh(); | ||
99 | check_http(); | ||
100 | check_netlink(); | ||
101 | } | ||
diff --git a/src/faudit/pid.c b/src/faudit/pid.c new file mode 100644 index 000000000..a0fb1d921 --- /dev/null +++ b/src/faudit/pid.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "faudit.h" | ||
21 | |||
22 | void pid_test(void) { | ||
23 | char *kern_proc[] = { | ||
24 | "kthreadd", | ||
25 | "ksoftirqd", | ||
26 | "kworker", | ||
27 | "rcu_sched", | ||
28 | "rcu_bh", | ||
29 | NULL // NULL terminated list | ||
30 | }; | ||
31 | int i; | ||
32 | |||
33 | // look at the first 10 processes | ||
34 | int not_visible = 1; | ||
35 | for (i = 1; i <= 10; i++) { | ||
36 | struct stat s; | ||
37 | char *fname; | ||
38 | if (asprintf(&fname, "/proc/%d/comm", i) == -1) | ||
39 | errExit("asprintf"); | ||
40 | if (stat(fname, &s) == -1) { | ||
41 | free(fname); | ||
42 | continue; | ||
43 | } | ||
44 | |||
45 | // open file | ||
46 | /* coverity[toctou] */ | ||
47 | FILE *fp = fopen(fname, "r"); | ||
48 | if (!fp) { | ||
49 | // fprintf(stderr, "Warning: cannot open %s\n", fname); | ||
50 | free(fname); | ||
51 | continue; | ||
52 | } | ||
53 | |||
54 | // read file | ||
55 | char buf[100]; | ||
56 | if (fgets(buf, 10, fp) == NULL) { | ||
57 | // fprintf(stderr, "Warning: cannot read %s\n", fname); | ||
58 | fclose(fp); | ||
59 | free(fname); | ||
60 | continue; | ||
61 | } | ||
62 | not_visible = 0; | ||
63 | |||
64 | // clean /n | ||
65 | char *ptr; | ||
66 | if ((ptr = strchr(buf, '\n')) != NULL) | ||
67 | *ptr = '\0'; | ||
68 | |||
69 | // check process name against the kernel list | ||
70 | int j = 0; | ||
71 | while (kern_proc[j] != NULL) { | ||
72 | if (strncmp(buf, kern_proc[j], strlen(kern_proc[j])) == 0) { | ||
73 | fclose(fp); | ||
74 | free(fname); | ||
75 | printf("BAD: Process %d is not running in a PID namespace. ", getpid()); | ||
76 | printf("Are you sure you're running in a sandbox?\n"); | ||
77 | return; | ||
78 | } | ||
79 | j++; | ||
80 | } | ||
81 | |||
82 | fclose(fp); | ||
83 | free(fname); | ||
84 | } | ||
85 | |||
86 | pid_t pid = getpid(); | ||
87 | if (not_visible && pid > 100) | ||
88 | printf("BAD: Process %d is not running in a PID namespace.\n", pid); | ||
89 | else | ||
90 | printf("GOOD: process %d is running in a PID namespace.\n", pid); | ||
91 | |||
92 | // try to guess the type of container/sandbox | ||
93 | char *str = getenv("container"); | ||
94 | if (str) | ||
95 | printf("INFO: container/sandbox %s.\n", str); | ||
96 | else { | ||
97 | str = getenv("SNAP"); | ||
98 | if (str) | ||
99 | printf("INFO: this is a snap package\n"); | ||
100 | } | ||
101 | } | ||
diff --git a/src/faudit/seccomp.c b/src/faudit/seccomp.c new file mode 100644 index 000000000..7b2999467 --- /dev/null +++ b/src/faudit/seccomp.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "faudit.h" | ||
21 | |||
22 | #define MAXBUF 4098 | ||
23 | static int extract_seccomp(int *val) { | ||
24 | FILE *fp = fopen("/proc/self/status", "r"); | ||
25 | if (!fp) | ||
26 | return 1; | ||
27 | |||
28 | char buf[MAXBUF]; | ||
29 | while (fgets(buf, MAXBUF, fp)) { | ||
30 | if (strncmp(buf, "Seccomp:\t", 8) == 0) { | ||
31 | char *ptr = buf + 8; | ||
32 | int tmp; | ||
33 | sscanf(ptr, "%d", &tmp); | ||
34 | *val = tmp; | ||
35 | fclose(fp); | ||
36 | return 0; | ||
37 | } | ||
38 | } | ||
39 | |||
40 | fclose(fp); | ||
41 | return 1; | ||
42 | } | ||
43 | |||
44 | void seccomp_test(void) { | ||
45 | int seccomp_status; | ||
46 | int rv = extract_seccomp(&seccomp_status); | ||
47 | |||
48 | if (rv) { | ||
49 | printf("INFO: cannot extract seccomp configuration on this platform.\n"); | ||
50 | return; | ||
51 | } | ||
52 | |||
53 | if (seccomp_status == 0) { | ||
54 | printf("BAD: seccomp disabled. Use \"firejail --seccomp\" to enable it.\n"); | ||
55 | } | ||
56 | else if (seccomp_status == 1) | ||
57 | printf("GOOD: seccomp strict mode - only read, write, _exit, and sigreturn are allowd.\n"); | ||
58 | else if (seccomp_status == 2) { | ||
59 | printf("GOOD: seccomp BPF enabled.\n"); | ||
60 | |||
61 | printf("checking syscalls: "); fflush(0); | ||
62 | printf("mount... "); fflush(0); | ||
63 | syscall_run("mount"); | ||
64 | |||
65 | printf("umount2... "); fflush(0); | ||
66 | syscall_run("umount2"); | ||
67 | |||
68 | printf("ptrace... "); fflush(0); | ||
69 | syscall_run("ptrace"); | ||
70 | |||
71 | printf("swapon... "); fflush(0); | ||
72 | syscall_run("swapon"); | ||
73 | |||
74 | printf("swapoff... "); fflush(0); | ||
75 | syscall_run("swapoff"); | ||
76 | |||
77 | printf("init_module... "); fflush(0); | ||
78 | syscall_run("init_module"); | ||
79 | |||
80 | printf("delete_module... "); fflush(0); | ||
81 | syscall_run("delete_module"); | ||
82 | |||
83 | printf("chroot... "); fflush(0); | ||
84 | syscall_run("chroot"); | ||
85 | |||
86 | printf("pivot_root... "); fflush(0); | ||
87 | syscall_run("pivot_root"); | ||
88 | |||
89 | #if defined(__i386__) || defined(__x86_64__) | ||
90 | printf("iopl... "); fflush(0); | ||
91 | syscall_run("iopl"); | ||
92 | |||
93 | printf("ioperm... "); fflush(0); | ||
94 | syscall_run("ioperm"); | ||
95 | #endif | ||
96 | printf("\n"); | ||
97 | } | ||
98 | else | ||
99 | fprintf(stderr, "Error: unrecognized seccomp mode\n"); | ||
100 | |||
101 | } | ||
diff --git a/src/faudit/syscall.c b/src/faudit/syscall.c new file mode 100644 index 000000000..3c87305df --- /dev/null +++ b/src/faudit/syscall.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "faudit.h" | ||
21 | #include <sys/ptrace.h> | ||
22 | #include <sys/swap.h> | ||
23 | #if defined(__i386__) || defined(__x86_64__) | ||
24 | #include <sys/io.h> | ||
25 | #endif | ||
26 | #include <sys/wait.h> | ||
27 | extern int init_module(void *module_image, unsigned long len, | ||
28 | const char *param_values); | ||
29 | extern int finit_module(int fd, const char *param_values, | ||
30 | int flags); | ||
31 | extern int delete_module(const char *name, int flags); | ||
32 | extern int pivot_root(const char *new_root, const char *put_old); | ||
33 | |||
34 | void syscall_helper(int argc, char **argv) { | ||
35 | (void) argc; | ||
36 | |||
37 | if (strcmp(argv[2], "mount") == 0) { | ||
38 | mount(NULL, NULL, NULL, 0, NULL); | ||
39 | printf("\nUGLY: mount syscall permitted.\n"); | ||
40 | } | ||
41 | else if (strcmp(argv[2], "umount2") == 0) { | ||
42 | umount2(NULL, 0); | ||
43 | printf("\nUGLY: umount2 syscall permitted.\n"); | ||
44 | } | ||
45 | else if (strcmp(argv[2], "ptrace") == 0) { | ||
46 | ptrace(0, 0, NULL, NULL); | ||
47 | printf("\nUGLY: ptrace syscall permitted.\n"); | ||
48 | } | ||
49 | else if (strcmp(argv[2], "swapon") == 0) { | ||
50 | swapon(NULL, 0); | ||
51 | printf("\nUGLY: swapon syscall permitted.\n"); | ||
52 | } | ||
53 | else if (strcmp(argv[2], "swapoff") == 0) { | ||
54 | swapoff(NULL); | ||
55 | printf("\nUGLY: swapoff syscall permitted.\n"); | ||
56 | } | ||
57 | else if (strcmp(argv[2], "init_module") == 0) { | ||
58 | init_module(NULL, 0, NULL); | ||
59 | printf("\nUGLY: init_module syscall permitted.\n"); | ||
60 | } | ||
61 | else if (strcmp(argv[2], "delete_module") == 0) { | ||
62 | delete_module(NULL, 0); | ||
63 | printf("\nUGLY: delete_module syscall permitted.\n"); | ||
64 | } | ||
65 | else if (strcmp(argv[2], "chroot") == 0) { | ||
66 | int rv = chroot("/blablabla-57281292"); | ||
67 | (void) rv; | ||
68 | printf("\nUGLY: chroot syscall permitted.\n"); | ||
69 | } | ||
70 | else if (strcmp(argv[2], "pivot_root") == 0) { | ||
71 | pivot_root(NULL, NULL); | ||
72 | printf("\nUGLY: pivot_root syscall permitted.\n"); | ||
73 | } | ||
74 | #if defined(__i386__) || defined(__x86_64__) | ||
75 | else if (strcmp(argv[2], "iopl") == 0) { | ||
76 | iopl(0L); | ||
77 | printf("\nUGLY: iopl syscall permitted.\n"); | ||
78 | } | ||
79 | else if (strcmp(argv[2], "ioperm") == 0) { | ||
80 | ioperm(0, 0, 0); | ||
81 | printf("\nUGLY: ioperm syscall permitted.\n"); | ||
82 | } | ||
83 | #endif | ||
84 | exit(0); | ||
85 | } | ||
86 | |||
87 | void syscall_run(const char *name) { | ||
88 | assert(prog); | ||
89 | |||
90 | pid_t child = fork(); | ||
91 | if (child < 0) | ||
92 | errExit("fork"); | ||
93 | if (child == 0) { | ||
94 | execl(prog, prog, "syscall", name, NULL); | ||
95 | perror("execl"); | ||
96 | _exit(1); | ||
97 | } | ||
98 | |||
99 | // wait for the child to finish | ||
100 | waitpid(child, NULL, 0); | ||
101 | } | ||
diff --git a/src/faudit/x11.c b/src/faudit/x11.c new file mode 100644 index 000000000..43f40f4e9 --- /dev/null +++ b/src/faudit/x11.c | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "faudit.h" | ||
21 | #include <sys/socket.h> | ||
22 | #include <dirent.h> | ||
23 | |||
24 | |||
25 | void x11_test(void) { | ||
26 | // check regular display 0 sockets | ||
27 | if (check_unix("/tmp/.X11-unix/X0") == 0) | ||
28 | printf("MAYBE: X11 socket /tmp/.X11-unix/X0 is available\n"); | ||
29 | |||
30 | if (check_unix("@/tmp/.X11-unix/X0") == 0) | ||
31 | printf("MAYBE: X11 socket @/tmp/.X11-unix/X0 is available\n"); | ||
32 | |||
33 | // check all unix sockets in /tmp/.X11-unix directory | ||
34 | DIR *dir; | ||
35 | if (!(dir = opendir("/tmp/.X11-unix"))) { | ||
36 | // sleep 2 seconds and try again | ||
37 | sleep(2); | ||
38 | if (!(dir = opendir("/tmp/.X11-unix"))) { | ||
39 | ; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | if (dir == NULL) | ||
44 | printf("GOOD: cannot open /tmp/.X11-unix directory\n"); | ||
45 | else { | ||
46 | struct dirent *entry; | ||
47 | while ((entry = readdir(dir)) != NULL) { | ||
48 | if (strcmp(entry->d_name, "X0") == 0) | ||
49 | continue; | ||
50 | if (strcmp(entry->d_name, ".") == 0) | ||
51 | continue; | ||
52 | if (strcmp(entry->d_name, "..") == 0) | ||
53 | continue; | ||
54 | char *name; | ||
55 | if (asprintf(&name, "/tmp/.X11-unix/%s", entry->d_name) == -1) | ||
56 | errExit("asprintf"); | ||
57 | if (check_unix(name) == 0) | ||
58 | printf("MAYBE: X11 socket %s is available\n", name); | ||
59 | free(name); | ||
60 | } | ||
61 | closedir(dir); | ||
62 | } | ||
63 | } | ||
diff --git a/src/firecfg/Makefile.in b/src/firecfg/Makefile.in index 11f8b1e8d..f9fe08768 100644 --- a/src/firecfg/Makefile.in +++ b/src/firecfg/Makefile.in | |||
@@ -16,22 +16,24 @@ HAVE_NETWORK=@HAVE_NETWORK@ | |||
16 | HAVE_USERNS=@HAVE_USERNS@ | 16 | HAVE_USERNS=@HAVE_USERNS@ |
17 | HAVE_X11=@HAVE_X11@ | 17 | HAVE_X11=@HAVE_X11@ |
18 | HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ | 18 | HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ |
19 | HAVE_GCOV=@HAVE_GCOV@ | ||
20 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
19 | 21 | ||
20 | 22 | ||
21 | H_FILE_LIST = $(sort $(wildcard *.[h])) | 23 | H_FILE_LIST = $(sort $(wildcard *.[h])) |
22 | C_FILE_LIST = $(sort $(wildcard *.c)) | 24 | C_FILE_LIST = $(sort $(wildcard *.c)) |
23 | OBJS = $(C_FILE_LIST:.c=.o) | 25 | OBJS = $(C_FILE_LIST:.c=.o) |
24 | BINOBJS = $(foreach file, $(OBJS), $file) | 26 | BINOBJS = $(foreach file, $(OBJS), $file) |
25 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_SECCOMP) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | 27 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_SECCOMP) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security |
26 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | 28 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread |
27 | 29 | ||
28 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h | 30 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h |
29 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | 31 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ |
30 | 32 | ||
31 | firecfg: $(OBJS) ../lib/common.o | 33 | firecfg: $(OBJS) ../lib/common.o |
32 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o $(LIBS) | 34 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) |
33 | 35 | ||
34 | clean:; rm -f *.o firecfg firecfg.1 firecfg.1.gz | 36 | clean:; rm -f *.o firecfg firecfg.1 firecfg.1.gz *.gcov *.gcda *.gcno |
35 | 37 | ||
36 | distclean: clean | 38 | distclean: clean |
37 | rm -fr Makefile | 39 | rm -fr Makefile |
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config index c28f8e352..e3e333497 100644 --- a/src/firecfg/firecfg.config +++ b/src/firecfg/firecfg.config | |||
@@ -2,44 +2,124 @@ | |||
2 | # This is the list of programs handled by firecfg utility | 2 | # This is the list of programs handled by firecfg utility |
3 | # | 3 | # |
4 | 4 | ||
5 | # astronomy | ||
6 | gpredict | ||
7 | stellarium | ||
8 | |||
9 | # bittorrent/ftp | ||
10 | deluge | ||
11 | dropbox | ||
12 | filezilla | ||
13 | qbittorrent | ||
14 | rtorrent | ||
15 | transmission-gtk | ||
16 | transmission-qt | ||
17 | uget-gtk | ||
18 | |||
5 | # browsers/email | 19 | # browsers/email |
6 | firefox | 20 | abrowser |
7 | iceweasel | 21 | brave |
8 | chromium-browser | ||
9 | chromium | 22 | chromium |
23 | chromium-browser | ||
10 | conkeror | 24 | conkeror |
11 | thunderbird | 25 | cyberfox |
12 | epiphany | 26 | firefox |
27 | firefox-esr | ||
13 | flashpeak-slimjet | 28 | flashpeak-slimjet |
29 | epiphany | ||
30 | dillo | ||
31 | google-chrome | ||
14 | google-chrome-beta | 32 | google-chrome-beta |
15 | google-chrome-stable | 33 | google-chrome-stable |
16 | google-chrome-unstable | 34 | google-chrome-unstable |
17 | google-chrome | 35 | iceweasel |
18 | icecat | 36 | icecat |
19 | icedove | 37 | icedove |
20 | kmail | 38 | kmail |
21 | midori | 39 | midori |
40 | netsurf | ||
22 | opera-beta | 41 | opera-beta |
23 | opera | 42 | opera |
43 | palemoon | ||
24 | qutebrowser | 44 | qutebrowser |
45 | start-tor-browser | ||
25 | seamonkey | 46 | seamonkey |
26 | seamonkey-bin | 47 | seamonkey-bin |
48 | thunderbird | ||
27 | vivaldi-beta | 49 | vivaldi-beta |
28 | vivaldi | 50 | vivaldi |
29 | dillo | 51 | evolution |
30 | 52 | ||
31 | # bittorrent/ftp | 53 | # chat/messaging |
32 | deluge | 54 | bitlbee |
33 | filezilla | 55 | corebird |
34 | qbittorrent | 56 | empathy |
35 | rtorrent | 57 | gitter |
36 | transmission-gtk | 58 | hexchat |
37 | transmission-qt | 59 | jitsi |
60 | konversation | ||
61 | pidgin | ||
62 | polari | ||
63 | psi-plus | ||
64 | qtox | ||
65 | quassel | ||
66 | skype | ||
67 | telegram | ||
68 | weechat | ||
69 | weechat-curses | ||
70 | xchat | ||
71 | |||
72 | # dns | ||
73 | dnscrypt-proxy | ||
74 | dnsmaq | ||
75 | unbound | ||
76 | |||
77 | # emulators/compatibility layers | ||
78 | mupen64plus | ||
79 | wine | ||
80 | dosbox | ||
81 | virtualbox | ||
82 | |||
83 | # games | ||
84 | 0ad | ||
85 | gnome-chess | ||
86 | hedgewars | ||
87 | steam | ||
88 | wesnot | ||
89 | warzone2100 | ||
90 | |||
91 | # Media | ||
92 | audacious | ||
93 | audacity | ||
94 | clementine | ||
95 | cmus | ||
96 | deadbeef | ||
97 | feh | ||
98 | gnome-mplayer | ||
99 | google-play-music-desktop-player | ||
100 | mpv | ||
101 | parole | ||
102 | rhythmbox | ||
103 | spotify | ||
104 | totem | ||
105 | vlc | ||
106 | xplayer | ||
107 | xviewer | ||
108 | eom | ||
109 | |||
110 | # news readers | ||
111 | quiterss | ||
38 | 112 | ||
39 | # office | 113 | # office |
114 | atril | ||
40 | cherrytree | 115 | cherrytree |
41 | evince | 116 | evince |
42 | fbreader | 117 | fbreader |
118 | gimp | ||
119 | gthumb | ||
120 | gwenview | ||
121 | inkscape | ||
122 | libreoffice | ||
43 | localc | 123 | localc |
44 | lodraw | 124 | lodraw |
45 | loffice | 125 | loffice |
@@ -48,29 +128,30 @@ loimpress | |||
48 | lomath | 128 | lomath |
49 | loweb | 129 | loweb |
50 | lowriter | 130 | lowriter |
131 | luminance-hdr | ||
132 | mupdf | ||
133 | qpdfview | ||
134 | soffice | ||
135 | synfigstudio | ||
51 | Mathematica | 136 | Mathematica |
52 | mathematica | 137 | mathematica |
138 | okular | ||
139 | pix | ||
140 | xpdf | ||
141 | xreader | ||
142 | zathura | ||
143 | openshot | ||
144 | flowblade | ||
145 | eog | ||
53 | 146 | ||
54 | # Media | 147 | # other |
55 | vlc | 148 | ssh |
56 | audacious | 149 | atom-beta |
57 | clementine | 150 | atom |
58 | deadbeef | 151 | ranger |
59 | parole | 152 | keepass |
60 | rhythmbox | 153 | keepassx |
61 | totem | 154 | xiphos |
62 | cmus | ||
63 | 155 | ||
64 | # chat/messaging | 156 | # weather/climate |
65 | bitlbee | 157 | aweather |
66 | empathy | ||
67 | gnome-mplayer | ||
68 | hexchat | ||
69 | pidgin | ||
70 | qtox | ||
71 | quassel | ||
72 | xchat | ||
73 | |||
74 | # games | ||
75 | hedgewars | ||
76 | wesnot | ||
diff --git a/src/firecfg/main.c b/src/firecfg/main.c index 70d29a3ed..d2566ce22 100644 --- a/src/firecfg/main.c +++ b/src/firecfg/main.c | |||
@@ -24,8 +24,13 @@ | |||
24 | #include <dirent.h> | 24 | #include <dirent.h> |
25 | #include <sys/types.h> | 25 | #include <sys/types.h> |
26 | #include <sys/stat.h> | 26 | #include <sys/stat.h> |
27 | #include <fcntl.h> | ||
27 | #include <unistd.h> | 28 | #include <unistd.h> |
29 | #include <string.h> | ||
30 | #include <errno.h> | ||
31 | #include <sys/mman.h> | ||
28 | #include "../include/common.h" | 32 | #include "../include/common.h" |
33 | static int arg_debug = 0; | ||
29 | 34 | ||
30 | static void usage(void) { | 35 | static void usage(void) { |
31 | printf("firecfg - version %s\n\n", VERSION); | 36 | printf("firecfg - version %s\n\n", VERSION); |
@@ -37,8 +42,10 @@ static void usage(void) { | |||
37 | printf("DESKTOP INTEGRATION section in man 1 firejail.\n\n"); | 42 | printf("DESKTOP INTEGRATION section in man 1 firejail.\n\n"); |
38 | printf("Usage: firecfg [OPTIONS]\n\n"); | 43 | printf("Usage: firecfg [OPTIONS]\n\n"); |
39 | printf(" --clean - remove all firejail symbolic links.\n\n"); | 44 | printf(" --clean - remove all firejail symbolic links.\n\n"); |
45 | printf(" --debug - print debug messages.\n\n"); | ||
40 | printf(" --help, -? - this help screen.\n\n"); | 46 | printf(" --help, -? - this help screen.\n\n"); |
41 | printf(" --list - list all firejail symbolic links.\n\n"); | 47 | printf(" --list - list all firejail symbolic links.\n\n"); |
48 | printf(" --fix - fix .desktop files.\n\n"); | ||
42 | printf(" --version - print program version and exit.\n\n"); | 49 | printf(" --version - print program version and exit.\n\n"); |
43 | printf("Example:\n\n"); | 50 | printf("Example:\n\n"); |
44 | printf(" $ sudo firecfg\n"); | 51 | printf(" $ sudo firecfg\n"); |
@@ -49,10 +56,14 @@ static void usage(void) { | |||
49 | printf(" /usr/local/bin/firefox\n"); | 56 | printf(" /usr/local/bin/firefox\n"); |
50 | printf(" /usr/local/bin/vlc\n"); | 57 | printf(" /usr/local/bin/vlc\n"); |
51 | printf(" [...]\n"); | 58 | printf(" [...]\n"); |
52 | printf(" $ sudo firecfg --clear\n"); | 59 | printf(" $ sudo firecfg --clean\n"); |
53 | printf(" /usr/local/bin/firefox removed\n"); | 60 | printf(" /usr/local/bin/firefox removed\n"); |
54 | printf(" /usr/local/bin/vlc removed\n"); | 61 | printf(" /usr/local/bin/vlc removed\n"); |
55 | printf(" [...]\n"); | 62 | printf(" [...]\n"); |
63 | printf(" $ firecfg --fix\n"); | ||
64 | printf(" /home/user/.local/share/applications/chromium.desktop created\n"); | ||
65 | printf(" /home/user/.local/share/applications/vlc.desktop created\n"); | ||
66 | printf(" [...]\n"); | ||
56 | printf("\n"); | 67 | printf("\n"); |
57 | printf("License GPL version 2 or later\n"); | 68 | printf("License GPL version 2 or later\n"); |
58 | printf("Homepage: http://firejail.wordpress.com\n\n"); | 69 | printf("Homepage: http://firejail.wordpress.com\n\n"); |
@@ -67,9 +78,12 @@ static int find(const char *program, const char *directory) { | |||
67 | errExit("asprintf"); | 78 | errExit("asprintf"); |
68 | 79 | ||
69 | struct stat s; | 80 | struct stat s; |
70 | if (stat(fname, &s) == 0) | 81 | if (stat(fname, &s) == 0) { |
82 | if (arg_debug) | ||
83 | printf("found %s in directory %s\n", program, directory); | ||
71 | retval = 1; | 84 | retval = 1; |
72 | 85 | } | |
86 | |||
73 | free(fname); | 87 | free(fname); |
74 | return retval; | 88 | return retval; |
75 | } | 89 | } |
@@ -79,7 +93,8 @@ static int find(const char *program, const char *directory) { | |||
79 | static int which(const char *program) { | 93 | static int which(const char *program) { |
80 | // check some well-known paths | 94 | // check some well-known paths |
81 | if (find(program, "/bin") || find(program, "/usr/bin") || | 95 | if (find(program, "/bin") || find(program, "/usr/bin") || |
82 | find(program, "/sbin") || find(program, "/usr/sbin")) | 96 | find(program, "/sbin") || find(program, "/usr/sbin") || |
97 | find(program, "/usr/games")) | ||
83 | return 1; | 98 | return 1; |
84 | 99 | ||
85 | // check environment | 100 | // check environment |
@@ -205,8 +220,9 @@ static void set_file(const char *name, const char *firejail_exec) { | |||
205 | errExit("asprintf"); | 220 | errExit("asprintf"); |
206 | 221 | ||
207 | struct stat s; | 222 | struct stat s; |
208 | if (stat(fname, &s) == 0) | 223 | if (stat(fname, &s) == 0) { |
209 | ; //printf("%s already present\n", fname); | 224 | printf("%s is already present, skipping...\n", fname); |
225 | } | ||
210 | else { | 226 | else { |
211 | int rv = symlink(firejail_exec, fname); | 227 | int rv = symlink(firejail_exec, fname); |
212 | if (rv) { | 228 | if (rv) { |
@@ -268,7 +284,7 @@ static void set(void) { | |||
268 | // empty line | 284 | // empty line |
269 | if (*start == '\0') | 285 | if (*start == '\0') |
270 | continue; | 286 | continue; |
271 | 287 | ||
272 | // set link | 288 | // set link |
273 | set_file(start, firejail_exec); | 289 | set_file(start, firejail_exec); |
274 | } | 290 | } |
@@ -278,6 +294,164 @@ static void set(void) { | |||
278 | free(firejail_exec); | 294 | free(firejail_exec); |
279 | } | 295 | } |
280 | 296 | ||
297 | static void fix_desktop_files(void) { | ||
298 | if (getuid() == 0) { | ||
299 | fprintf(stderr, "Error: you should run --fix as user\n"); | ||
300 | exit(1); | ||
301 | } | ||
302 | |||
303 | char *homedir = getenv("HOME"); | ||
304 | if (!homedir) | ||
305 | errExit("getenv"); | ||
306 | |||
307 | char *user_apps_dir; | ||
308 | if (asprintf(&user_apps_dir, "%s/.local/share/applications", homedir) == -1) | ||
309 | errExit("asprintf"); | ||
310 | |||
311 | DIR *dir = opendir("/usr/share/applications"); | ||
312 | if (!dir) { | ||
313 | perror("Error: cannot open /usr/share/applications directory"); | ||
314 | exit(1); | ||
315 | } | ||
316 | |||
317 | if (chdir("/usr/share/applications")) { | ||
318 | perror("Error: cannot chdir to /usr/share/applications"); | ||
319 | exit(1); | ||
320 | } | ||
321 | |||
322 | struct dirent *entry; | ||
323 | while ((entry = readdir(dir)) != NULL) { | ||
324 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) | ||
325 | continue; | ||
326 | |||
327 | // skip if not regular file or link | ||
328 | if (entry->d_type != DT_REG && entry->d_type != DT_LNK) | ||
329 | continue; | ||
330 | |||
331 | // skip if not .desktop file | ||
332 | if (strstr(entry->d_name,".desktop") != (entry->d_name+strlen(entry->d_name)-8)) | ||
333 | continue; | ||
334 | |||
335 | char *filename = entry->d_name; | ||
336 | |||
337 | // skip links | ||
338 | if (is_link(filename)) | ||
339 | continue; | ||
340 | |||
341 | struct stat sb; | ||
342 | if (stat(filename, &sb) == -1) | ||
343 | errExit("stat"); | ||
344 | |||
345 | int fd = open(filename, O_RDONLY); | ||
346 | if (fd == -1) | ||
347 | errExit("open"); | ||
348 | |||
349 | char *buf = mmap(NULL, sb.st_size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); | ||
350 | if (buf == MAP_FAILED) | ||
351 | errExit("mmap"); | ||
352 | |||
353 | close(fd); | ||
354 | |||
355 | // check format | ||
356 | if (strstr(buf, "[Desktop Entry]\n") == NULL) { | ||
357 | if (arg_debug) | ||
358 | fprintf(stderr, "/usr/share/applications/%s - SKIPPED: wrong format?\n", filename); | ||
359 | munmap(buf, sb.st_size + 1); | ||
360 | continue; | ||
361 | } | ||
362 | |||
363 | // get executable name | ||
364 | char *ptr1 = strstr(buf,"\nExec="); | ||
365 | if (!ptr1 || strlen(ptr1) < 7) { | ||
366 | if (arg_debug) | ||
367 | fprintf(stderr, "/usr/share/applications/%s - SKIPPED: wrong format?\n", filename); | ||
368 | munmap(buf, sb.st_size + 1); | ||
369 | continue; | ||
370 | } | ||
371 | |||
372 | char *execname = ptr1 + 6; | ||
373 | // https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html | ||
374 | // The executable program can either be specified with its full path | ||
375 | // or with the name of the executable only | ||
376 | if (execname[0] != '/') { | ||
377 | if (arg_debug) | ||
378 | fprintf(stderr, "/usr/share/applications/%s - already OK\n", filename); | ||
379 | continue; | ||
380 | } | ||
381 | // executable name can be quoted, this is rare and currently unsupported, TODO | ||
382 | if (execname[0] == '"') { | ||
383 | if (arg_debug) | ||
384 | fprintf(stderr, "/usr/share/applications/%s - skipped: path quoting unsupported\n", filename); | ||
385 | continue; | ||
386 | } | ||
387 | |||
388 | // put '\0' at end of filename | ||
389 | char *tail = NULL; | ||
390 | char endchar = ' '; | ||
391 | if (execname[0] == '/') { | ||
392 | char *ptr2 = index(execname, ' '); | ||
393 | char *ptr3 = index(execname, '\n'); | ||
394 | if (ptr2 && (!ptr3 || (ptr2 < ptr3))) { | ||
395 | endchar = ptr2[0]; | ||
396 | ptr2[0] = '\0'; | ||
397 | tail = ptr2 + 1; | ||
398 | } else if (ptr3 && (!ptr2 || (ptr3 < ptr2))) { | ||
399 | endchar = ptr3[0]; | ||
400 | ptr3[0] = '\0'; | ||
401 | tail = ptr3 + 1; | ||
402 | } | ||
403 | ptr1[5] = '\0'; | ||
404 | } | ||
405 | |||
406 | char *bname = basename(execname); | ||
407 | assert(bname); | ||
408 | |||
409 | // check if basename in PATH | ||
410 | if (!which(bname)) { | ||
411 | fprintf(stderr, "/usr/share/applications/%s - skipped, %s not in PATH\n", filename, bname); | ||
412 | continue; | ||
413 | } | ||
414 | |||
415 | char *outname; | ||
416 | if (asprintf(&outname ,"%s/%s", user_apps_dir, filename) == -1) | ||
417 | errExit("asprintf"); | ||
418 | |||
419 | int fd1 = open(outname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR); | ||
420 | free(outname); | ||
421 | |||
422 | if (fd1 == -1) { | ||
423 | fprintf(stderr, "%s/%s skipped: %s\n", user_apps_dir, filename, strerror(errno)); | ||
424 | munmap(buf, sb.st_size + 1); | ||
425 | continue; | ||
426 | } | ||
427 | |||
428 | FILE *outfile = fdopen(fd1, "w"); | ||
429 | if (!outfile) { | ||
430 | fprintf(stderr, "%s/%s skipped: %s\n", user_apps_dir, filename, strerror(errno)); | ||
431 | munmap(buf, sb.st_size + 1); | ||
432 | close(fd1); | ||
433 | continue; | ||
434 | } | ||
435 | |||
436 | if (fprintf(outfile,\ | ||
437 | "# Converted by firecfg --fix from /usr/share/applications/%s\n\n%s=%s%c%s",\ | ||
438 | filename, buf, bname, endchar, tail) < 0) { | ||
439 | fprintf(stderr, "Unable to write %s/%s: %s\n", user_apps_dir, filename, strerror(errno)); | ||
440 | munmap(buf, sb.st_size + 1); | ||
441 | fclose(outfile); | ||
442 | continue; | ||
443 | } | ||
444 | |||
445 | fclose(outfile); | ||
446 | munmap(buf, sb.st_size + 1); | ||
447 | |||
448 | printf("%s/%s created\n", user_apps_dir, filename); | ||
449 | } | ||
450 | |||
451 | closedir(dir); | ||
452 | free(user_apps_dir); | ||
453 | } | ||
454 | |||
281 | int main(int argc, char **argv) { | 455 | int main(int argc, char **argv) { |
282 | int i; | 456 | int i; |
283 | 457 | ||
@@ -288,6 +462,8 @@ int main(int argc, char **argv) { | |||
288 | usage(); | 462 | usage(); |
289 | return 0; | 463 | return 0; |
290 | } | 464 | } |
465 | else if (strcmp(argv[i], "--debug") == 0) | ||
466 | arg_debug = 1; | ||
291 | else if (strcmp(argv[i], "--version") == 0) { | 467 | else if (strcmp(argv[i], "--version") == 0) { |
292 | printf("firecfg version %s\n\n", VERSION); | 468 | printf("firecfg version %s\n\n", VERSION); |
293 | return 0; | 469 | return 0; |
@@ -300,6 +476,10 @@ int main(int argc, char **argv) { | |||
300 | list(); | 476 | list(); |
301 | return 0; | 477 | return 0; |
302 | } | 478 | } |
479 | else if (strcmp(argv[i], "--fix") == 0) { | ||
480 | fix_desktop_files(); | ||
481 | return 0; | ||
482 | } | ||
303 | else { | 483 | else { |
304 | fprintf(stderr, "Error: invalid command line option\n"); | 484 | fprintf(stderr, "Error: invalid command line option\n"); |
305 | usage(); | 485 | usage(); |
diff --git a/src/firejail/Makefile.in b/src/firejail/Makefile.in index 3ad4ba75e..6e5071925 100644 --- a/src/firejail/Makefile.in +++ b/src/firejail/Makefile.in | |||
@@ -16,22 +16,28 @@ HAVE_NETWORK=@HAVE_NETWORK@ | |||
16 | HAVE_USERNS=@HAVE_USERNS@ | 16 | HAVE_USERNS=@HAVE_USERNS@ |
17 | HAVE_X11=@HAVE_X11@ | 17 | HAVE_X11=@HAVE_X11@ |
18 | HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ | 18 | HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ |
19 | 19 | HAVE_WHITELIST=@HAVE_WHITELIST@ | |
20 | HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ | ||
21 | HAVE_APPARMOR=@HAVE_APPARMOR@ | ||
22 | HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ | ||
23 | HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@ | ||
24 | HAVE_GCOV=@HAVE_GCOV@ | ||
25 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
20 | 26 | ||
21 | H_FILE_LIST = $(sort $(wildcard *.[h])) | 27 | H_FILE_LIST = $(sort $(wildcard *.[h])) |
22 | C_FILE_LIST = $(sort $(wildcard *.c)) | 28 | C_FILE_LIST = $(sort $(wildcard *.c)) |
23 | OBJS = $(C_FILE_LIST:.c=.o) | 29 | OBJS = $(C_FILE_LIST:.c=.o) |
24 | BINOBJS = $(foreach file, $(OBJS), $file) | 30 | BINOBJS = $(foreach file, $(OBJS), $file) |
25 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_SECCOMP) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | 31 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security |
26 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | 32 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread |
27 | 33 | ||
28 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h | 34 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/pid.h ../include/seccomp.h ../include/syscall.h |
29 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | 35 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ |
30 | 36 | ||
31 | firejail: $(OBJS) ../lib/libnetlink.o ../lib/common.o | 37 | firejail: $(OBJS) ../lib/libnetlink.o ../lib/common.o |
32 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS) | 38 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) |
33 | 39 | ||
34 | clean:; rm -f *.o firejail firejail.1 firejail.1.gz | 40 | clean:; rm -f *.o firejail firejail.1 firejail.1.gz *.gcov *.gcda *.gcno |
35 | 41 | ||
36 | distclean: clean | 42 | distclean: clean |
37 | rm -fr Makefile | 43 | rm -fr Makefile |
diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c new file mode 100644 index 000000000..a658173eb --- /dev/null +++ b/src/firejail/appimage.c | |||
@@ -0,0 +1,176 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | // http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=770fe30a46a12b6fb6b63fbe1737654d28e84844 | ||
21 | // sudo mount -o loop krita-3.0-x86_64.appimage mnt | ||
22 | |||
23 | #include "firejail.h" | ||
24 | #include <sys/types.h> | ||
25 | #include <sys/stat.h> | ||
26 | #include <sys/mount.h> | ||
27 | #include <fcntl.h> | ||
28 | #include <linux/loop.h> | ||
29 | #include <errno.h> | ||
30 | |||
31 | static char *devloop = NULL; // device file | ||
32 | static char *mntdir = NULL; // mount point in /tmp directory | ||
33 | |||
34 | const char *appimage_getdir(void) { | ||
35 | return mntdir; | ||
36 | } | ||
37 | |||
38 | void appimage_set(const char *appimage_path) { | ||
39 | assert(appimage_path); | ||
40 | assert(devloop == NULL); // don't call this twice! | ||
41 | EUID_ASSERT(); | ||
42 | |||
43 | #ifdef LOOP_CTL_GET_FREE // test for older kernels; this definition is found in /usr/include/linux/loop.h | ||
44 | // check appimage_path | ||
45 | if (access(appimage_path, R_OK) == -1) { | ||
46 | fprintf(stderr, "Error: cannot access AppImage file\n"); | ||
47 | exit(1); | ||
48 | } | ||
49 | |||
50 | // get appimage type and ELF size | ||
51 | // a value of 0 means we are dealing with a type1 appimage | ||
52 | long unsigned int size = appimage2_size(appimage_path); | ||
53 | if (arg_debug) | ||
54 | printf("AppImage ELF size %lu\n", size); | ||
55 | |||
56 | // open as user to prevent race condition | ||
57 | int ffd = open(appimage_path, O_RDONLY|O_CLOEXEC); | ||
58 | if (ffd == -1) { | ||
59 | fprintf(stderr, "Error: /dev/loop-control interface is not supported by your kernel\n"); | ||
60 | exit(1); | ||
61 | } | ||
62 | |||
63 | // find or allocate a free loop device to use | ||
64 | EUID_ROOT(); | ||
65 | int cfd = open("/dev/loop-control", O_RDWR); | ||
66 | int devnr = ioctl(cfd, LOOP_CTL_GET_FREE); | ||
67 | if (devnr == -1) { | ||
68 | fprintf(stderr, "Error: cannot allocate a new loopback device\n"); | ||
69 | exit(1); | ||
70 | } | ||
71 | close(cfd); | ||
72 | if (asprintf(&devloop, "/dev/loop%d", devnr) == -1) | ||
73 | errExit("asprintf"); | ||
74 | |||
75 | int lfd = open(devloop, O_RDONLY); | ||
76 | if (ioctl(lfd, LOOP_SET_FD, ffd) == -1) { | ||
77 | fprintf(stderr, "Error: cannot configure the loopback device\n"); | ||
78 | exit(1); | ||
79 | } | ||
80 | |||
81 | if (size) { | ||
82 | struct loop_info64 info; | ||
83 | memset(&info, 0, sizeof(struct loop_info64)); | ||
84 | info.lo_offset = size; | ||
85 | if (ioctl(lfd, LOOP_SET_STATUS64, &info) == -1) | ||
86 | errExit("configure appimage offset"); | ||
87 | } | ||
88 | |||
89 | close(lfd); | ||
90 | close(ffd); | ||
91 | EUID_USER(); | ||
92 | |||
93 | // creates appimage mount point perms 0700 | ||
94 | if (asprintf(&mntdir, "%s/.appimage-%u", RUN_FIREJAIL_APPIMAGE_DIR, getpid()) == -1) | ||
95 | errExit("asprintf"); | ||
96 | EUID_ROOT(); | ||
97 | mkdir_attr(mntdir, 0700, getuid(), getgid()); | ||
98 | EUID_USER(); | ||
99 | |||
100 | // mount | ||
101 | char *mode; | ||
102 | if (asprintf(&mode, "mode=700,uid=%d,gid=%d", getuid(), getgid()) == -1) | ||
103 | errExit("asprintf"); | ||
104 | EUID_ROOT(); | ||
105 | |||
106 | if (size == 0) { | ||
107 | if (mount(devloop, mntdir, "iso9660",MS_MGC_VAL|MS_RDONLY, mode) < 0) | ||
108 | errExit("mounting appimage"); | ||
109 | } | ||
110 | else { | ||
111 | if (mount(devloop, mntdir, "squashfs",MS_MGC_VAL|MS_RDONLY, mode) < 0) | ||
112 | errExit("mounting appimage"); | ||
113 | } | ||
114 | |||
115 | if (arg_debug) | ||
116 | printf("appimage mounted on %s\n", mntdir); | ||
117 | EUID_USER(); | ||
118 | |||
119 | // set environment | ||
120 | if (appimage_path && setenv("APPIMAGE", appimage_path, 1) < 0) | ||
121 | errExit("setenv"); | ||
122 | if (mntdir && setenv("APPDIR", mntdir, 1) < 0) | ||
123 | errExit("setenv"); | ||
124 | |||
125 | // build new command line | ||
126 | if (asprintf(&cfg.command_line, "%s/AppRun", mntdir) == -1) | ||
127 | errExit("asprintf"); | ||
128 | |||
129 | free(mode); | ||
130 | #ifdef HAVE_GCOV | ||
131 | __gcov_flush(); | ||
132 | #endif | ||
133 | #else | ||
134 | fprintf(stderr, "Error: /dev/loop-control interface is not supported by your kernel\n"); | ||
135 | exit(1); | ||
136 | #endif | ||
137 | } | ||
138 | |||
139 | void appimage_clear(void) { | ||
140 | int rv; | ||
141 | |||
142 | EUID_ROOT(); | ||
143 | if (mntdir) { | ||
144 | int i; | ||
145 | int rv = 0; | ||
146 | for (i = 0; i < 5; i++) { | ||
147 | rv = umount2(mntdir, MNT_FORCE); | ||
148 | if (rv == 0) | ||
149 | break; | ||
150 | if (rv == -1 && errno == EBUSY) { | ||
151 | if (!arg_quiet) | ||
152 | printf("Warning: EBUSY error trying to unmount %s\n", mntdir); | ||
153 | sleep(2); | ||
154 | continue; | ||
155 | } | ||
156 | |||
157 | // rv = -1 | ||
158 | if (!arg_quiet) { | ||
159 | printf("Warning: error trying to unmount %s\n", mntdir); | ||
160 | perror("umount"); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | if (rv == 0) { | ||
165 | rmdir(mntdir); | ||
166 | free(mntdir); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | if (devloop) { | ||
171 | int lfd = open(devloop, O_RDONLY); | ||
172 | rv = ioctl(lfd, LOOP_CLR_FD, 0); | ||
173 | (void) rv; | ||
174 | close(lfd); | ||
175 | } | ||
176 | } | ||
diff --git a/src/firejail/appimage_size.c b/src/firejail/appimage_size.c new file mode 100644 index 000000000..3f5c3150c --- /dev/null +++ b/src/firejail/appimage_size.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | /* | ||
21 | Compile with: | ||
22 | gcc elfsize.c -o elfsize | ||
23 | Example: | ||
24 | ls -l 126584 | ||
25 | Calculation using the values also reported by readelf -h: | ||
26 | Start of section headers e_shoff 124728 | ||
27 | Size of section headers e_shentsize 64 | ||
28 | Number of section headers e_shnum 29 | ||
29 | e_shoff + ( e_shentsize * e_shnum ) = 126584 | ||
30 | */ | ||
31 | #include <elf.h> | ||
32 | #include <byteswap.h> | ||
33 | #include <stdio.h> | ||
34 | #include <stdint.h> | ||
35 | #include <errno.h> | ||
36 | #include <stdlib.h> | ||
37 | #include <unistd.h> | ||
38 | #include <string.h> | ||
39 | #include <fcntl.h> | ||
40 | |||
41 | typedef Elf32_Nhdr Elf_Nhdr; | ||
42 | |||
43 | static Elf64_Ehdr ehdr; | ||
44 | |||
45 | #if __BYTE_ORDER == __LITTLE_ENDIAN | ||
46 | #define ELFDATANATIVE ELFDATA2LSB | ||
47 | #elif __BYTE_ORDER == __BIG_ENDIAN | ||
48 | #define ELFDATANATIVE ELFDATA2MSB | ||
49 | #else | ||
50 | #error "Unknown machine endian" | ||
51 | #endif | ||
52 | |||
53 | static uint16_t file16_to_cpu(uint16_t val) { | ||
54 | if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) | ||
55 | val = bswap_16(val); | ||
56 | return val; | ||
57 | } | ||
58 | |||
59 | |||
60 | static uint32_t file32_to_cpu(uint32_t val) { | ||
61 | if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) | ||
62 | val = bswap_32(val); | ||
63 | return val; | ||
64 | } | ||
65 | |||
66 | |||
67 | static uint64_t file64_to_cpu(uint64_t val) { | ||
68 | if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) | ||
69 | val = bswap_64(val); | ||
70 | return val; | ||
71 | } | ||
72 | |||
73 | |||
74 | // return 0 if error | ||
75 | static long unsigned int read_elf32(int fd) { | ||
76 | Elf32_Ehdr ehdr32; | ||
77 | ssize_t ret; | ||
78 | |||
79 | ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); | ||
80 | if (ret < 0 || (size_t)ret != sizeof(ehdr)) | ||
81 | return 0; | ||
82 | |||
83 | ehdr.e_shoff = file32_to_cpu(ehdr32.e_shoff); | ||
84 | ehdr.e_shentsize = file16_to_cpu(ehdr32.e_shentsize); | ||
85 | ehdr.e_shnum = file16_to_cpu(ehdr32.e_shnum); | ||
86 | |||
87 | return(ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shnum)); | ||
88 | } | ||
89 | |||
90 | |||
91 | // return 0 if error | ||
92 | static long unsigned int read_elf64(int fd) { | ||
93 | Elf64_Ehdr ehdr64; | ||
94 | ssize_t ret; | ||
95 | |||
96 | ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); | ||
97 | if (ret < 0 || (size_t)ret != sizeof(ehdr)) | ||
98 | return 0; | ||
99 | |||
100 | ehdr.e_shoff = file64_to_cpu(ehdr64.e_shoff); | ||
101 | ehdr.e_shentsize = file16_to_cpu(ehdr64.e_shentsize); | ||
102 | ehdr.e_shnum = file16_to_cpu(ehdr64.e_shnum); | ||
103 | |||
104 | return(ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shnum)); | ||
105 | } | ||
106 | |||
107 | |||
108 | // return 0 if error | ||
109 | // return 0 if this is not an appimgage2 file | ||
110 | long unsigned int appimage2_size(const char *fname) { | ||
111 | /* TODO, FIXME: This assumes that the section header table (SHT) is | ||
112 | the last part of the ELF. This is usually the case but | ||
113 | it could also be that the last section is the last part | ||
114 | of the ELF. This should be checked for. | ||
115 | */ | ||
116 | ssize_t ret; | ||
117 | int fd; | ||
118 | long unsigned int size = 0; | ||
119 | |||
120 | fd = open(fname, O_RDONLY); | ||
121 | if (fd < 0) | ||
122 | return 0; | ||
123 | |||
124 | ret = pread(fd, ehdr.e_ident, EI_NIDENT, 0); | ||
125 | if (ret != EI_NIDENT) | ||
126 | goto getout; | ||
127 | |||
128 | if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) && | ||
129 | (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) | ||
130 | goto getout; | ||
131 | |||
132 | if(ehdr.e_ident[EI_CLASS] == ELFCLASS32) { | ||
133 | size = read_elf32(fd); | ||
134 | } | ||
135 | else if(ehdr.e_ident[EI_CLASS] == ELFCLASS64) { | ||
136 | size = read_elf64(fd); | ||
137 | } | ||
138 | else { | ||
139 | goto getout; | ||
140 | } | ||
141 | if (size == 0) | ||
142 | goto getout; | ||
143 | |||
144 | |||
145 | // look for a LZMA header at this location | ||
146 | unsigned char buf[4]; | ||
147 | ret = pread(fd, buf, 4, size); | ||
148 | if (ret != 4) { | ||
149 | size = 0; | ||
150 | goto getout; | ||
151 | } | ||
152 | if (memcmp(buf, "hsqs", 4) != 0) | ||
153 | size = 0; | ||
154 | |||
155 | getout: | ||
156 | close(fd); | ||
157 | return size; | ||
158 | } | ||
159 | |||
160 | |||
diff --git a/src/firejail/arp.c b/src/firejail/arp.c index fb5e426b0..ddb75905f 100644 --- a/src/firejail/arp.c +++ b/src/firejail/arp.c | |||
@@ -40,6 +40,7 @@ typedef struct arp_hdr_t { | |||
40 | uint8_t target_ip[4]; | 40 | uint8_t target_ip[4]; |
41 | } ArpHdr; | 41 | } ArpHdr; |
42 | 42 | ||
43 | |||
43 | // returns 0 if the address is not in use, -1 otherwise | 44 | // returns 0 if the address is not in use, -1 otherwise |
44 | int arp_check(const char *dev, uint32_t destaddr, uint32_t srcaddr) { | 45 | int arp_check(const char *dev, uint32_t destaddr, uint32_t srcaddr) { |
45 | if (strlen(dev) > IFNAMSIZ) { | 46 | if (strlen(dev) > IFNAMSIZ) { |
@@ -286,189 +287,4 @@ uint32_t arp_assign(const char *dev, Bridge *br) { | |||
286 | return ip; | 287 | return ip; |
287 | } | 288 | } |
288 | 289 | ||
289 | // scan interface (--scan option) | ||
290 | void arp_scan(const char *dev, uint32_t ifip, uint32_t ifmask) { | ||
291 | assert(dev); | ||
292 | assert(ifip); | ||
293 | |||
294 | // printf("Scanning interface %s (%d.%d.%d.%d/%d)\n", | ||
295 | // dev, PRINT_IP(ifip & ifmask), mask2bits(ifmask)); | ||
296 | |||
297 | if (strlen(dev) > IFNAMSIZ) { | ||
298 | fprintf(stderr, "Error: invalid network device name %s\n", dev); | ||
299 | exit(1); | ||
300 | } | ||
301 | |||
302 | // find interface mac address | ||
303 | int sock; | ||
304 | if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) | ||
305 | errExit("socket"); | ||
306 | struct ifreq ifr; | ||
307 | memset(&ifr, 0, sizeof (ifr)); | ||
308 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); | ||
309 | if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) | ||
310 | errExit("ioctl"); | ||
311 | close(sock); | ||
312 | uint8_t mac[6]; | ||
313 | memcpy (mac, ifr.ifr_hwaddr.sa_data, 6); | ||
314 | |||
315 | // open layer2 socket | ||
316 | if ((sock = socket(PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) | ||
317 | errExit("socket"); | ||
318 | |||
319 | // try all possible ip addresses in ascending order | ||
320 | uint32_t range = ~ifmask + 1; // the number of potential addresses | ||
321 | // this software is not supported for /31 networks | ||
322 | if (range < 4) { | ||
323 | fprintf(stderr, "Warning: this option is not supported for /31 networks\n"); | ||
324 | close(sock); | ||
325 | return; | ||
326 | } | ||
327 | |||
328 | uint32_t dest = (ifip & ifmask) + 1; | ||
329 | uint32_t last = dest + range - 1; | ||
330 | uint32_t src = htonl(ifip); | ||
331 | |||
332 | // wait not more than one second for an answer | ||
333 | int header_printed = 0; | ||
334 | uint32_t last_ip = 0; | ||
335 | struct timeval ts; | ||
336 | ts.tv_sec = 2; // 2 seconds receive timeout | ||
337 | ts.tv_usec = 0; | ||
338 | |||
339 | while (1) { | ||
340 | fd_set rfds; | ||
341 | FD_ZERO(&rfds); | ||
342 | FD_SET(sock, &rfds); | ||
343 | fd_set wfds; | ||
344 | FD_ZERO(&wfds); | ||
345 | FD_SET(sock, &wfds); | ||
346 | int maxfd = sock; | ||
347 | |||
348 | uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc | ||
349 | memset(frame, 0, ETH_FRAME_LEN); | ||
350 | |||
351 | int nready; | ||
352 | if (dest < last) | ||
353 | nready = select(maxfd + 1, &rfds, &wfds, (fd_set *) 0, NULL); | ||
354 | else | ||
355 | nready = select(maxfd + 1, &rfds, (fd_set *) 0, (fd_set *) 0, &ts); | ||
356 | |||
357 | if (nready < 0) | ||
358 | errExit("select"); | ||
359 | |||
360 | if (nready == 0) { // timeout | ||
361 | break; | ||
362 | } | ||
363 | |||
364 | if (FD_ISSET(sock, &wfds) && dest < last) { | ||
365 | // configure layer2 socket address information | ||
366 | struct sockaddr_ll addr; | ||
367 | memset(&addr, 0, sizeof(addr)); | ||
368 | if ((addr.sll_ifindex = if_nametoindex(dev)) == 0) | ||
369 | errExit("if_nametoindex"); | ||
370 | addr.sll_family = AF_PACKET; | ||
371 | memcpy (addr.sll_addr, mac, 6); | ||
372 | addr.sll_halen = htons(6); | ||
373 | |||
374 | // build the arp packet header | ||
375 | ArpHdr hdr; | ||
376 | memset(&hdr, 0, sizeof(hdr)); | ||
377 | hdr.htype = htons(1); | ||
378 | hdr.ptype = htons(ETH_P_IP); | ||
379 | hdr.hlen = 6; | ||
380 | hdr.plen = 4; | ||
381 | hdr.opcode = htons(1); //ARPOP_REQUEST | ||
382 | memcpy(hdr.sender_mac, mac, 6); | ||
383 | memcpy(hdr.sender_ip, (uint8_t *)&src, 4); | ||
384 | uint32_t dst = htonl(dest); | ||
385 | memcpy(hdr.target_ip, (uint8_t *)&dst, 4); | ||
386 | |||
387 | // build ethernet frame | ||
388 | uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc | ||
389 | memset(frame, 0, sizeof(frame)); | ||
390 | frame[0] = frame[1] = frame[2] = frame[3] = frame[4] = frame[5] = 0xff; | ||
391 | memcpy(frame + 6, mac, 6); | ||
392 | frame[12] = ETH_P_ARP / 256; | ||
393 | frame[13] = ETH_P_ARP % 256; | ||
394 | memcpy (frame + 14, &hdr, sizeof(hdr)); | ||
395 | |||
396 | // send packet | ||
397 | int len; | ||
398 | if ((len = sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr))) <= 0) | ||
399 | errExit("send"); | ||
400 | //printf("send %d bytes to %d.%d.%d.%d\n", len, PRINT_IP(dest)); | ||
401 | fflush(0); | ||
402 | dest++; | ||
403 | } | ||
404 | |||
405 | if (FD_ISSET(sock, &rfds)) { | ||
406 | // read the incoming packet | ||
407 | int len = recvfrom(sock, frame, ETH_FRAME_LEN, 0, NULL, NULL); | ||
408 | if (len < 0) { | ||
409 | perror("recvfrom"); | ||
410 | } | ||
411 | |||
412 | // parse the incoming packet | ||
413 | if ((unsigned int) len < 14 + sizeof(ArpHdr)) | ||
414 | continue; | ||
415 | |||
416 | // look only at ARP packets | ||
417 | if (frame[12] != (ETH_P_ARP / 256) || frame[13] != (ETH_P_ARP % 256)) | ||
418 | continue; | ||
419 | |||
420 | ArpHdr hdr; | ||
421 | memcpy(&hdr, frame + 14, sizeof(ArpHdr)); | ||
422 | |||
423 | if (hdr.opcode == htons(2)) { | ||
424 | // check my mac and my address | ||
425 | if (memcmp(mac, hdr.target_mac, 6) != 0) | ||
426 | continue; | ||
427 | uint32_t ip; | ||
428 | memcpy(&ip, hdr.target_ip, 4); | ||
429 | if (ip != src) | ||
430 | continue; | ||
431 | memcpy(&ip, hdr.sender_ip, 4); | ||
432 | ip = ntohl(ip); | ||
433 | |||
434 | if (ip == last_ip) // filter duplicates | ||
435 | continue; | ||
436 | last_ip = ip; | ||
437 | |||
438 | // printing | ||
439 | if (header_printed == 0) { | ||
440 | printf(" Network scan:\n"); | ||
441 | |||
442 | // print parent interface | ||
443 | if (cfg.bridge0.configured && cfg.bridge0.ip && cfg.bridge0.macvlan && | ||
444 | (cfg.bridge0.ip & cfg.bridge0.mask) == (ifip & cfg.bridge0.mask)) | ||
445 | printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", | ||
446 | PRINT_MAC(cfg.bridge0.mac), PRINT_IP(cfg.bridge0.ip)); | ||
447 | |||
448 | if (cfg.bridge1.configured && cfg.bridge1.ip && cfg.bridge1.macvlan && | ||
449 | (cfg.bridge1.ip & cfg.bridge1.mask) == (ifip & cfg.bridge1.mask)) | ||
450 | printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", | ||
451 | PRINT_MAC(cfg.bridge1.mac), PRINT_IP(cfg.bridge1.ip)); | ||
452 | |||
453 | if (cfg.bridge2.configured && cfg.bridge2.ip && cfg.bridge2.macvlan && | ||
454 | (cfg.bridge2.ip & cfg.bridge2.mask) == (ifip & cfg.bridge2.mask)) | ||
455 | printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", | ||
456 | PRINT_MAC(cfg.bridge2.mac), PRINT_IP(cfg.bridge2.ip)); | ||
457 | |||
458 | if (cfg.bridge3.configured && cfg.bridge3.ip && cfg.bridge3.macvlan && | ||
459 | (cfg.bridge3.ip & cfg.bridge3.mask) == (ifip & cfg.bridge3.mask)) | ||
460 | printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", | ||
461 | PRINT_MAC(cfg.bridge3.mac), PRINT_IP(cfg.bridge3.ip)); | ||
462 | |||
463 | header_printed = 1; | ||
464 | } | ||
465 | printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", | ||
466 | PRINT_MAC(hdr.sender_mac), PRINT_IP(ip)); | ||
467 | } | ||
468 | } | ||
469 | } | ||
470 | |||
471 | close(sock); | ||
472 | } | ||
473 | |||
474 | 290 | ||
diff --git a/src/firejail/bandwidth.c b/src/firejail/bandwidth.c index 34c5ca509..5e9002f22 100644 --- a/src/firejail/bandwidth.c +++ b/src/firejail/bandwidth.c | |||
@@ -130,14 +130,8 @@ static void bandwidth_create_run_file(pid_t pid) { | |||
130 | /* coverity[toctou] */ | 130 | /* coverity[toctou] */ |
131 | FILE *fp = fopen(fname, "w"); | 131 | FILE *fp = fopen(fname, "w"); |
132 | if (fp) { | 132 | if (fp) { |
133 | SET_PERMS_STREAM(fp, 0, 0, 0644); | ||
133 | fclose(fp); | 134 | fclose(fp); |
134 | |||
135 | /* coverity[toctou] */ | ||
136 | if (chmod(fname, 0644) == -1) | ||
137 | errExit("chmod"); | ||
138 | /* coverity[toctou] */ | ||
139 | if (chown(fname, 0, 0) == -1) | ||
140 | errExit("chown"); | ||
141 | } | 135 | } |
142 | else { | 136 | else { |
143 | fprintf(stderr, "Error: cannot create bandwidth file\n"); | 137 | fprintf(stderr, "Error: cannot create bandwidth file\n"); |
@@ -180,12 +174,9 @@ void network_set_run_file(pid_t pid) { | |||
180 | fprintf(fp, "%s:%s\n", cfg.bridge2.dev, cfg.bridge2.devsandbox); | 174 | fprintf(fp, "%s:%s\n", cfg.bridge2.dev, cfg.bridge2.devsandbox); |
181 | if (cfg.bridge3.configured) | 175 | if (cfg.bridge3.configured) |
182 | fprintf(fp, "%s:%s\n", cfg.bridge3.dev, cfg.bridge3.devsandbox); | 176 | fprintf(fp, "%s:%s\n", cfg.bridge3.dev, cfg.bridge3.devsandbox); |
183 | fclose(fp); | ||
184 | 177 | ||
185 | if (chmod(fname, 0644) == -1) | 178 | SET_PERMS_STREAM(fp, 0, 0, 0644); |
186 | errExit("chmod"); | 179 | fclose(fp); |
187 | if (chown(fname, 0, 0) == -1) | ||
188 | errExit("chown"); | ||
189 | } | 180 | } |
190 | else { | 181 | else { |
191 | fprintf(stderr, "Error: cannot create network map file\n"); | 182 | fprintf(stderr, "Error: cannot create network map file\n"); |
@@ -320,21 +311,6 @@ void bandwidth_set(pid_t pid, const char *dev, int down, int up) { | |||
320 | //*********************************** | 311 | //*********************************** |
321 | // command execution | 312 | // command execution |
322 | //*********************************** | 313 | //*********************************** |
323 | void bandwidth_name(const char *name, const char *command, const char *dev, int down, int up) { | ||
324 | EUID_ASSERT(); | ||
325 | if (!name || strlen(name) == 0) { | ||
326 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
327 | exit(1); | ||
328 | } | ||
329 | pid_t pid; | ||
330 | if (name2pid(name, &pid)) { | ||
331 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
332 | exit(1); | ||
333 | } | ||
334 | |||
335 | bandwidth_pid(pid, command, dev, down, up); | ||
336 | } | ||
337 | |||
338 | void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up) { | 314 | void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up) { |
339 | EUID_ASSERT(); | 315 | EUID_ASSERT(); |
340 | //************************ | 316 | //************************ |
@@ -459,13 +435,21 @@ void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, in | |||
459 | if (setregid(0, 0)) | 435 | if (setregid(0, 0)) |
460 | errExit("setregid"); | 436 | errExit("setregid"); |
461 | 437 | ||
438 | if (!cfg.shell) | ||
439 | cfg.shell = guess_shell(); | ||
440 | if (!cfg.shell) { | ||
441 | fprintf(stderr, "Error: no POSIX shell found, please use --shell command line option\n"); | ||
442 | exit(1); | ||
443 | } | ||
444 | |||
462 | char *arg[4]; | 445 | char *arg[4]; |
463 | arg[0] = "/bin/bash"; | 446 | arg[0] = cfg.shell; |
464 | arg[1] = "-c"; | 447 | arg[1] = "-c"; |
465 | arg[2] = cmd; | 448 | arg[2] = cmd; |
466 | arg[3] = NULL; | 449 | arg[3] = NULL; |
467 | execvp("/bin/bash", arg); | 450 | clearenv(); |
451 | execvp(arg[0], arg); | ||
468 | 452 | ||
469 | // it will never get here | 453 | // it will never get here |
470 | exit(0); | 454 | errExit("execvp"); |
471 | } | 455 | } |
diff --git a/src/firejail/caps.c b/src/firejail/caps.c index 2d42c7d8a..ba811cada 100644 --- a/src/firejail/caps.c +++ b/src/firejail/caps.c | |||
@@ -168,17 +168,6 @@ static CapsEntry capslist[] = { | |||
168 | // | 168 | // |
169 | }; // end of capslist | 169 | }; // end of capslist |
170 | 170 | ||
171 | const 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 | 171 | // return -1 if error, or syscall number |
183 | static int caps_find_name(const char *name) { | 172 | static int caps_find_name(const char *name) { |
184 | int i; | 173 | int i; |
@@ -397,26 +386,10 @@ static uint64_t extract_caps(int pid) { | |||
397 | } | 386 | } |
398 | fclose(fp); | 387 | fclose(fp); |
399 | free(file); | 388 | free(file); |
400 | printf("Error: cannot read caps configuration\n"); | 389 | fprintf(stderr, "Error: cannot read caps configuration\n"); |
401 | exit(1); | 390 | exit(1); |
402 | } | 391 | } |
403 | 392 | ||
404 | |||
405 | void caps_print_filter_name(const char *name) { | ||
406 | EUID_ASSERT(); | ||
407 | if (!name || strlen(name) == 0) { | ||
408 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
409 | exit(1); | ||
410 | } | ||
411 | pid_t pid; | ||
412 | if (name2pid(name, &pid)) { | ||
413 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
414 | exit(1); | ||
415 | } | ||
416 | |||
417 | caps_print_filter(pid); | ||
418 | } | ||
419 | |||
420 | void caps_print_filter(pid_t pid) { | 393 | void caps_print_filter(pid_t pid) { |
421 | EUID_ASSERT(); | 394 | EUID_ASSERT(); |
422 | 395 | ||
diff --git a/src/firejail/cgroup.c b/src/firejail/cgroup.c index ebd87f0d2..d9c7af9cf 100644 --- a/src/firejail/cgroup.c +++ b/src/firejail/cgroup.c | |||
@@ -30,10 +30,9 @@ void save_cgroup(void) { | |||
30 | if (fp) { | 30 | if (fp) { |
31 | fprintf(fp, "%s", cfg.cgroup); | 31 | fprintf(fp, "%s", cfg.cgroup); |
32 | fflush(0); | 32 | fflush(0); |
33 | SET_PERMS_STREAM(fp, 0, 0, 0644); | ||
33 | if (fclose(fp)) | 34 | if (fclose(fp)) |
34 | goto errout; | 35 | goto errout; |
35 | if (chown(RUN_CGROUP_CFG, 0, 0) < 0) | ||
36 | errExit("chown"); | ||
37 | } | 36 | } |
38 | else | 37 | else |
39 | goto errout; | 38 | goto errout; |
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c index 430b0c5a6..78c0e5c60 100644 --- a/src/firejail/checkcfg.c +++ b/src/firejail/checkcfg.c | |||
@@ -19,15 +19,17 @@ | |||
19 | */ | 19 | */ |
20 | #include "firejail.h" | 20 | #include "firejail.h" |
21 | #include <sys/stat.h> | 21 | #include <sys/stat.h> |
22 | #include <linux/loop.h> | ||
22 | 23 | ||
23 | #define MAX_READ 8192 // line buffer for profile files | 24 | #define MAX_READ 8192 // line buffer for profile files |
24 | 25 | ||
25 | static int initialized = 0; | 26 | static int initialized = 0; |
26 | static int cfg_val[CFG_MAX]; | 27 | static int cfg_val[CFG_MAX]; |
27 | char *xephyr_screen = "800x600"; | 28 | char *xephyr_screen = "800x600"; |
29 | char *xephyr_extra_params = ""; | ||
30 | char *netfilter_default = NULL; | ||
28 | 31 | ||
29 | int checkcfg(int val) { | 32 | int checkcfg(int val) { |
30 | EUID_ASSERT(); | ||
31 | assert(val < CFG_MAX); | 33 | assert(val < CFG_MAX); |
32 | int line = 0; | 34 | int line = 0; |
33 | 35 | ||
@@ -37,6 +39,8 @@ int checkcfg(int val) { | |||
37 | for (i = 0; i < CFG_MAX; i++) | 39 | for (i = 0; i < CFG_MAX; i++) |
38 | cfg_val[i] = 1; // most of them are enabled by default | 40 | cfg_val[i] = 1; // most of them are enabled by default |
39 | cfg_val[CFG_RESTRICTED_NETWORK] = 0; // disabled by default | 41 | cfg_val[CFG_RESTRICTED_NETWORK] = 0; // disabled by default |
42 | cfg_val[CFG_FORCE_NONEWPRIVS] = 0; // disabled by default | ||
43 | cfg_val[CFG_PRIVATE_BIN_NO_LOCAL] = 0; // disabled by default | ||
40 | 44 | ||
41 | // open configuration file | 45 | // open configuration file |
42 | char *fname; | 46 | char *fname; |
@@ -45,10 +49,24 @@ int checkcfg(int val) { | |||
45 | 49 | ||
46 | FILE *fp = fopen(fname, "r"); | 50 | FILE *fp = fopen(fname, "r"); |
47 | if (!fp) { | 51 | if (!fp) { |
52 | #ifdef HAVE_GLOBALCFG | ||
48 | fprintf(stderr, "Error: Firejail configuration file %s not found\n", fname); | 53 | fprintf(stderr, "Error: Firejail configuration file %s not found\n", fname); |
49 | exit(1); | 54 | exit(1); |
55 | #else | ||
56 | initialized = 1; | ||
57 | return cfg_val[val]; | ||
58 | #endif | ||
50 | } | 59 | } |
51 | 60 | ||
61 | // if the file exists, it should be owned by root | ||
62 | struct stat s; | ||
63 | if (stat(fname, &s) == -1) | ||
64 | errExit("stat"); | ||
65 | if (s.st_uid != 0) { | ||
66 | fprintf(stderr, "Error: configuration file should be owned by root\n"); | ||
67 | exit(1); | ||
68 | } | ||
69 | |||
52 | // read configuration file | 70 | // read configuration file |
53 | char buf[MAX_READ]; | 71 | char buf[MAX_READ]; |
54 | while (fgets(buf,MAX_READ, fp)) { | 72 | while (fgets(buf,MAX_READ, fp)) { |
@@ -106,6 +124,15 @@ int checkcfg(int val) { | |||
106 | else | 124 | else |
107 | goto errout; | 125 | goto errout; |
108 | } | 126 | } |
127 | // nonewprivs | ||
128 | else if (strncmp(ptr, "force-nonewprivs ", 17) == 0) { | ||
129 | if (strcmp(ptr + 17, "yes") == 0) | ||
130 | cfg_val[CFG_SECCOMP] = 1; | ||
131 | else if (strcmp(ptr + 17, "no") == 0) | ||
132 | cfg_val[CFG_SECCOMP] = 0; | ||
133 | else | ||
134 | goto errout; | ||
135 | } | ||
109 | // seccomp | 136 | // seccomp |
110 | else if (strncmp(ptr, "seccomp ", 8) == 0) { | 137 | else if (strncmp(ptr, "seccomp ", 8) == 0) { |
111 | if (strcmp(ptr + 8, "yes") == 0) | 138 | if (strcmp(ptr + 8, "yes") == 0) |
@@ -115,6 +142,15 @@ int checkcfg(int val) { | |||
115 | else | 142 | else |
116 | goto errout; | 143 | goto errout; |
117 | } | 144 | } |
145 | // whitelist | ||
146 | else if (strncmp(ptr, "whitelist ", 10) == 0) { | ||
147 | if (strcmp(ptr + 10, "yes") == 0) | ||
148 | cfg_val[CFG_WHITELIST] = 1; | ||
149 | else if (strcmp(ptr + 10, "no") == 0) | ||
150 | cfg_val[CFG_WHITELIST] = 0; | ||
151 | else | ||
152 | goto errout; | ||
153 | } | ||
118 | // network | 154 | // network |
119 | else if (strncmp(ptr, "network ", 8) == 0) { | 155 | else if (strncmp(ptr, "network ", 8) == 0) { |
120 | if (strcmp(ptr + 8, "yes") == 0) | 156 | if (strcmp(ptr + 8, "yes") == 0) |
@@ -133,6 +169,28 @@ int checkcfg(int val) { | |||
133 | else | 169 | else |
134 | goto errout; | 170 | goto errout; |
135 | } | 171 | } |
172 | // netfilter | ||
173 | else if (strncmp(ptr, "netfilter-default ", 18) == 0) { | ||
174 | char *fname = ptr + 18; | ||
175 | while (*fname == ' ' || *fname == '\t') | ||
176 | ptr++; | ||
177 | char *end = strchr(fname, ' '); | ||
178 | if (end) | ||
179 | *end = '\0'; | ||
180 | |||
181 | // is the file present? | ||
182 | struct stat s; | ||
183 | if (stat(fname, &s) == -1) { | ||
184 | fprintf(stderr, "Error: netfilter-default file %s not available\n", fname); | ||
185 | exit(1); | ||
186 | } | ||
187 | |||
188 | netfilter_default = strdup(fname); | ||
189 | if (!netfilter_default) | ||
190 | errExit("strdup"); | ||
191 | if (arg_debug) | ||
192 | printf("netfilter default file %s\n", fname); | ||
193 | } | ||
136 | 194 | ||
137 | // Xephyr screen size | 195 | // Xephyr screen size |
138 | else if (strncmp(ptr, "xephyr-screen ", 14) == 0) { | 196 | else if (strncmp(ptr, "xephyr-screen ", 14) == 0) { |
@@ -145,9 +203,73 @@ int checkcfg(int val) { | |||
145 | if (asprintf(&xephyr_screen, "%dx%d", n1, n2) == -1) | 203 | if (asprintf(&xephyr_screen, "%dx%d", n1, n2) == -1) |
146 | errExit("asprintf"); | 204 | errExit("asprintf"); |
147 | } | 205 | } |
206 | |||
207 | // xephyr window title | ||
208 | else if (strncmp(ptr, "xephyr-window-title ", 20) == 0) { | ||
209 | if (strcmp(ptr + 20, "yes") == 0) | ||
210 | cfg_val[CFG_XEPHYR_WINDOW_TITLE] = 1; | ||
211 | else if (strcmp(ptr + 20, "no") == 0) | ||
212 | cfg_val[CFG_XEPHYR_WINDOW_TITLE] = 0; | ||
213 | else | ||
214 | goto errout; | ||
215 | } | ||
216 | |||
217 | // Xephyr command extra parameters | ||
218 | else if (strncmp(ptr, "xephyr-extra-params ", 19) == 0) { | ||
219 | xephyr_extra_params = strdup(ptr + 19); | ||
220 | if (!xephyr_extra_params) | ||
221 | errExit("strdup"); | ||
222 | } | ||
223 | |||
224 | // quiet by default | ||
225 | else if (strncmp(ptr, "quiet-by-default ", 17) == 0) { | ||
226 | if (strcmp(ptr + 17, "yes") == 0) | ||
227 | arg_quiet = 1; | ||
228 | } | ||
229 | // remount /proc and /sys | ||
230 | else if (strncmp(ptr, "remount-proc-sys ", 17) == 0) { | ||
231 | if (strcmp(ptr + 17, "yes") == 0) | ||
232 | cfg_val[CFG_REMOUNT_PROC_SYS] = 1; | ||
233 | else if (strcmp(ptr + 17, "no") == 0) | ||
234 | cfg_val[CFG_REMOUNT_PROC_SYS] = 0; | ||
235 | else | ||
236 | goto errout; | ||
237 | } | ||
238 | else if (strncmp(ptr, "overlayfs ", 10) == 0) { | ||
239 | if (strcmp(ptr + 10, "yes") == 0) | ||
240 | cfg_val[CFG_OVERLAYFS] = 1; | ||
241 | else if (strcmp(ptr + 10, "no") == 0) | ||
242 | cfg_val[CFG_OVERLAYFS] = 0; | ||
243 | else | ||
244 | goto errout; | ||
245 | } | ||
246 | else if (strncmp(ptr, "private-home ", 13) == 0) { | ||
247 | if (strcmp(ptr + 13, "yes") == 0) | ||
248 | cfg_val[CFG_PRIVATE_HOME] = 1; | ||
249 | else if (strcmp(ptr + 13, "no") == 0) | ||
250 | cfg_val[CFG_PRIVATE_HOME] = 0; | ||
251 | else | ||
252 | goto errout; | ||
253 | } | ||
254 | else if (strncmp(ptr, "chroot-desktop ", 15) == 0) { | ||
255 | if (strcmp(ptr + 15, "yes") == 0) | ||
256 | cfg_val[CFG_CHROOT_DESKTOP] = 1; | ||
257 | else if (strcmp(ptr + 15, "no") == 0) | ||
258 | cfg_val[CFG_CHROOT_DESKTOP] = 0; | ||
259 | else | ||
260 | goto errout; | ||
261 | } | ||
262 | else if (strncmp(ptr, "private-bin-no-local ", 21) == 0) { | ||
263 | if (strcmp(ptr + 21, "yes") == 0) | ||
264 | cfg_val[CFG_PRIVATE_BIN_NO_LOCAL] = 1; | ||
265 | else if (strcmp(ptr + 21, "no") == 0) | ||
266 | cfg_val[CFG_PRIVATE_BIN_NO_LOCAL] = 0; | ||
267 | else | ||
268 | goto errout; | ||
269 | } | ||
148 | else | 270 | else |
149 | goto errout; | 271 | goto errout; |
150 | 272 | ||
151 | free(ptr); | 273 | free(ptr); |
152 | } | 274 | } |
153 | 275 | ||
@@ -163,3 +285,111 @@ errout: | |||
163 | exit(1); | 285 | exit(1); |
164 | } | 286 | } |
165 | 287 | ||
288 | |||
289 | void print_compiletime_support(void) { | ||
290 | printf("Compile time support:\n"); | ||
291 | printf("\t- AppArmor support is %s\n", | ||
292 | #ifdef HAVE_APPARMOR | ||
293 | "enabled" | ||
294 | #else | ||
295 | "disabled" | ||
296 | #endif | ||
297 | ); | ||
298 | |||
299 | printf("\t- AppImage support is %s\n", | ||
300 | #ifdef LOOP_CTL_GET_FREE // test for older kernels; this definition is found in /usr/include/linux/loop.h | ||
301 | "enabled" | ||
302 | #else | ||
303 | "disabled" | ||
304 | #endif | ||
305 | ); | ||
306 | |||
307 | |||
308 | |||
309 | |||
310 | |||
311 | printf("\t- bind support is %s\n", | ||
312 | #ifdef HAVE_BIND | ||
313 | "enabled" | ||
314 | #else | ||
315 | "disabled" | ||
316 | #endif | ||
317 | ); | ||
318 | |||
319 | printf("\t- chroot support is %s\n", | ||
320 | #ifdef HAVE_CHROOT | ||
321 | "enabled" | ||
322 | #else | ||
323 | "disabled" | ||
324 | #endif | ||
325 | ); | ||
326 | |||
327 | printf("\t- file and directory whitelisting support is %s\n", | ||
328 | #ifdef HAVE_WHITELIST | ||
329 | "enabled" | ||
330 | #else | ||
331 | "disabled" | ||
332 | #endif | ||
333 | ); | ||
334 | |||
335 | printf("\t- file transfer support is %s\n", | ||
336 | #ifdef HAVE_FILE_TRANSFER | ||
337 | "enabled" | ||
338 | #else | ||
339 | "disabled" | ||
340 | #endif | ||
341 | ); | ||
342 | |||
343 | printf("\t- networking support is %s\n", | ||
344 | #ifdef HAVE_NETWORK | ||
345 | "enabled" | ||
346 | #else | ||
347 | "disabled" | ||
348 | #endif | ||
349 | ); | ||
350 | |||
351 | |||
352 | #ifdef HAVE_NETWORK_RESTRICTED | ||
353 | printf("\t- networking features are available only to root user\n"); | ||
354 | #endif | ||
355 | |||
356 | printf("\t- overlayfs support is %s\n", | ||
357 | #ifdef HAVE_OVERLAYFS | ||
358 | "enabled" | ||
359 | #else | ||
360 | "disabled" | ||
361 | #endif | ||
362 | ); | ||
363 | |||
364 | printf("\t- private-home support is %s\n", | ||
365 | #ifdef HAVE_PRIVATE_HOME | ||
366 | "enabled" | ||
367 | #else | ||
368 | "disabled" | ||
369 | #endif | ||
370 | ); | ||
371 | |||
372 | printf("\t- seccomp-bpf support is %s\n", | ||
373 | #ifdef HAVE_SECCOMP | ||
374 | "enabled" | ||
375 | #else | ||
376 | "disabled" | ||
377 | #endif | ||
378 | ); | ||
379 | |||
380 | printf("\t- user namespace support is %s\n", | ||
381 | #ifdef HAVE_USERNS | ||
382 | "enabled" | ||
383 | #else | ||
384 | "disabled" | ||
385 | #endif | ||
386 | ); | ||
387 | |||
388 | printf("\t- X11 sandboxing support is %s\n", | ||
389 | #ifdef HAVE_X11 | ||
390 | "enabled" | ||
391 | #else | ||
392 | "disabled" | ||
393 | #endif | ||
394 | ); | ||
395 | } | ||
diff --git a/src/firejail/cmdline.c b/src/firejail/cmdline.c new file mode 100644 index 000000000..cadf4795d --- /dev/null +++ b/src/firejail/cmdline.c | |||
@@ -0,0 +1,159 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | |||
21 | #include "firejail.h" | ||
22 | #include <string.h> | ||
23 | #include <stdbool.h> | ||
24 | #include <stdio.h> | ||
25 | #include <linux/limits.h> | ||
26 | #include <assert.h> | ||
27 | #include <errno.h> | ||
28 | |||
29 | static int cmdline_length(int argc, char **argv, int index) { | ||
30 | assert(index != -1); | ||
31 | |||
32 | unsigned i,j; | ||
33 | int len = 0; | ||
34 | unsigned argcnt = argc - index; | ||
35 | bool in_quotes = false; | ||
36 | |||
37 | for (i = 0; i < argcnt; i++) { | ||
38 | in_quotes = false; | ||
39 | for (j = 0; j < strlen(argv[i + index]); j++) { | ||
40 | if (argv[i + index][j] == '\'') { | ||
41 | if (in_quotes) | ||
42 | len++; | ||
43 | if (j > 0 && argv[i + index][j-1] == '\'') | ||
44 | len++; | ||
45 | else | ||
46 | len += 3; | ||
47 | in_quotes = false; | ||
48 | } else { | ||
49 | if (!in_quotes) | ||
50 | len++; | ||
51 | len++; | ||
52 | in_quotes = true; | ||
53 | } | ||
54 | } | ||
55 | if (in_quotes) { | ||
56 | len++; | ||
57 | } | ||
58 | if (strlen(argv[i + index]) == 0) { | ||
59 | len += 2; | ||
60 | } | ||
61 | len++; | ||
62 | } | ||
63 | |||
64 | return len; | ||
65 | } | ||
66 | |||
67 | static void quote_cmdline(char *command_line, char *window_title, int len, int argc, char **argv, int index) { | ||
68 | assert(index != -1); | ||
69 | |||
70 | unsigned i,j; | ||
71 | unsigned argcnt = argc - index; | ||
72 | bool in_quotes = false; | ||
73 | char *ptr1 = command_line; | ||
74 | char *ptr2 = window_title; | ||
75 | |||
76 | for (i = 0; i < argcnt; i++) { | ||
77 | |||
78 | // enclose args by single quotes, | ||
79 | // and since single quote can't be represented in single quoted text | ||
80 | // each occurence of it should be enclosed by double quotes | ||
81 | in_quotes = false; | ||
82 | for (j = 0; j < strlen(argv[i + index]); j++) { | ||
83 | // single quote | ||
84 | if (argv[i + index][j] == '\'') { | ||
85 | if (in_quotes) { | ||
86 | // close quotes | ||
87 | ptr1[0] = '\''; | ||
88 | ptr1++; | ||
89 | } | ||
90 | // previous char was single quote too | ||
91 | if (j > 0 && argv[i + index][j-1] == '\'') { | ||
92 | ptr1--; | ||
93 | sprintf(ptr1, "\'\""); | ||
94 | } | ||
95 | // this first in series | ||
96 | else | ||
97 | { | ||
98 | sprintf(ptr1, "\"\'\""); | ||
99 | } | ||
100 | ptr1 += strlen(ptr1); | ||
101 | in_quotes = false; | ||
102 | } | ||
103 | // anything other | ||
104 | else | ||
105 | { | ||
106 | if (!in_quotes) { | ||
107 | // open quotes | ||
108 | ptr1[0] = '\''; | ||
109 | ptr1++; | ||
110 | } | ||
111 | ptr1[0] = argv[i + index][j]; | ||
112 | ptr1++; | ||
113 | in_quotes = true; | ||
114 | } | ||
115 | } | ||
116 | // close quotes | ||
117 | if (in_quotes) { | ||
118 | ptr1[0] = '\''; | ||
119 | ptr1++; | ||
120 | } | ||
121 | // handle empty argument case | ||
122 | if (strlen(argv[i + index]) == 0) { | ||
123 | sprintf(ptr1, "\'\'"); | ||
124 | ptr1 += strlen(ptr1); | ||
125 | } | ||
126 | // add space | ||
127 | sprintf(ptr1, " "); | ||
128 | ptr1 += strlen(ptr1); | ||
129 | |||
130 | sprintf(ptr2, "%s ", argv[i + index]); | ||
131 | ptr2 += strlen(ptr2); | ||
132 | } | ||
133 | |||
134 | assert((unsigned) len == strlen(command_line)); | ||
135 | } | ||
136 | |||
137 | void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index) { | ||
138 | // index == -1 could happen if we have --shell=none and no program was specified | ||
139 | // the program should exit with an error before entering this function | ||
140 | assert(index != -1); | ||
141 | |||
142 | int len = cmdline_length(argc, argv, index); | ||
143 | if (len > ARG_MAX) { | ||
144 | errno = E2BIG; | ||
145 | errExit("cmdline_length"); | ||
146 | } | ||
147 | |||
148 | *command_line = malloc(len + 1); | ||
149 | if (!*command_line) | ||
150 | errExit("malloc"); | ||
151 | *window_title = malloc(len + 1); | ||
152 | if (!*window_title) | ||
153 | errExit("malloc"); | ||
154 | |||
155 | quote_cmdline(*command_line, *window_title, len, argc, argv, index); | ||
156 | |||
157 | assert(*command_line); | ||
158 | assert(*window_title); | ||
159 | } | ||
diff --git a/src/firejail/cpu.c b/src/firejail/cpu.c index 1802ad5e1..7f53fed0f 100644 --- a/src/firejail/cpu.c +++ b/src/firejail/cpu.c | |||
@@ -78,11 +78,8 @@ void save_cpu(void) { | |||
78 | FILE *fp = fopen(RUN_CPU_CFG, "w"); | 78 | FILE *fp = fopen(RUN_CPU_CFG, "w"); |
79 | if (fp) { | 79 | if (fp) { |
80 | fprintf(fp, "%x\n", cfg.cpus); | 80 | fprintf(fp, "%x\n", cfg.cpus); |
81 | SET_PERMS_STREAM(fp, 0, 0, 0600); | ||
81 | fclose(fp); | 82 | fclose(fp); |
82 | if (chmod(RUN_CPU_CFG, 0600) < 0) | ||
83 | errExit("chmod"); | ||
84 | if (chown(RUN_CPU_CFG, 0, 0) < 0) | ||
85 | errExit("chown"); | ||
86 | } | 83 | } |
87 | else { | 84 | else { |
88 | fprintf(stderr, "Error: cannot save cpu affinity mask\n"); | 85 | fprintf(stderr, "Error: cannot save cpu affinity mask\n"); |
@@ -171,21 +168,6 @@ static void print_cpu(int pid) { | |||
171 | free(file); | 168 | free(file); |
172 | } | 169 | } |
173 | 170 | ||
174 | void cpu_print_filter_name(const char *name) { | ||
175 | EUID_ASSERT(); | ||
176 | if (!name || strlen(name) == 0) { | ||
177 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
178 | exit(1); | ||
179 | } | ||
180 | pid_t pid; | ||
181 | if (name2pid(name, &pid)) { | ||
182 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
183 | exit(1); | ||
184 | } | ||
185 | |||
186 | cpu_print_filter(pid); | ||
187 | } | ||
188 | |||
189 | void cpu_print_filter(pid_t pid) { | 171 | void cpu_print_filter(pid_t pid) { |
190 | EUID_ASSERT(); | 172 | EUID_ASSERT(); |
191 | 173 | ||
diff --git a/src/firejail/env.c b/src/firejail/env.c index 54a6b0036..a02c67ae1 100644 --- a/src/firejail/env.c +++ b/src/firejail/env.c | |||
@@ -27,12 +27,27 @@ typedef struct env_t { | |||
27 | struct env_t *next; | 27 | struct env_t *next; |
28 | char *name; | 28 | char *name; |
29 | char *value; | 29 | char *value; |
30 | ENV_OP op; | ||
30 | } Env; | 31 | } Env; |
31 | static Env *envlist = NULL; | 32 | static Env *envlist = NULL; |
32 | 33 | ||
33 | static void env_add(Env *env) { | 34 | static void env_add(Env *env) { |
34 | env->next = envlist; | 35 | env->next = NULL; |
35 | envlist = env; | 36 | |
37 | // add the new entry at the end of the list | ||
38 | if (envlist == NULL) { | ||
39 | envlist = env; | ||
40 | return; | ||
41 | } | ||
42 | |||
43 | Env *ptr = envlist; | ||
44 | while (1) { | ||
45 | if (ptr->next == NULL) { | ||
46 | ptr->next = env; | ||
47 | break; | ||
48 | } | ||
49 | ptr = ptr->next; | ||
50 | } | ||
36 | } | 51 | } |
37 | 52 | ||
38 | // load IBUS env variables | 53 | // load IBUS env variables |
@@ -87,7 +102,7 @@ void env_ibus_load(void) { | |||
87 | if (arg_debug) | 102 | if (arg_debug) |
88 | printf("%s\n", buf); | 103 | printf("%s\n", buf); |
89 | EUID_USER(); | 104 | EUID_USER(); |
90 | env_store(buf); | 105 | env_store(buf, SETENV); |
91 | EUID_ROOT(); | 106 | EUID_ROOT(); |
92 | } | 107 | } |
93 | 108 | ||
@@ -104,29 +119,31 @@ void env_defaults(void) { | |||
104 | // fix qt 4.8 | 119 | // fix qt 4.8 |
105 | if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0) | 120 | if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0) |
106 | errExit("setenv"); | 121 | errExit("setenv"); |
122 | // if (setenv("MOZ_NO_REMOTE, "1", 1) < 0) | ||
123 | // errExit("setenv"); | ||
107 | if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc, | 124 | if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc, |
108 | errExit("setenv"); | 125 | errExit("setenv"); |
109 | if (arg_zsh && setenv("SHELL", "/usr/bin/zsh", 1) < 0) | 126 | if (!cfg.shell) |
110 | errExit("setenv"); | 127 | cfg.shell = guess_shell(); |
111 | if (arg_csh && setenv("SHELL", "/bin/csh", 1) < 0) | ||
112 | errExit("setenv"); | ||
113 | if (cfg.shell && setenv("SHELL", cfg.shell, 1) < 0) | 128 | if (cfg.shell && setenv("SHELL", cfg.shell, 1) < 0) |
114 | errExit("setenv"); | 129 | errExit("setenv"); |
130 | |||
115 | // set prompt color to green | 131 | // set prompt color to green |
116 | //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' | 132 | char *prompt = getenv("FIREJAIL_PROMPT"); |
117 | if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) | 133 | if (prompt && strcmp(prompt, "yes") == 0) { |
118 | errExit("setenv"); | 134 | //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' |
135 | if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) | ||
136 | errExit("setenv"); | ||
137 | } | ||
119 | 138 | ||
120 | // build the window title and set it | 139 | // set the window title |
121 | char *title; | 140 | if (!arg_quiet) |
122 | if (asprintf(&title, "\033]0;firejail %s\007\n", cfg.window_title) == -1) | 141 | printf("\033]0;firejail %s\007", cfg.window_title); |
123 | errExit("asprintf"); | 142 | fflush(0); |
124 | printf("%s", title); | ||
125 | free(title); | ||
126 | } | 143 | } |
127 | 144 | ||
128 | // parse and store the environment setting | 145 | // parse and store the environment setting |
129 | void env_store(const char *str) { | 146 | void env_store(const char *str, ENV_OP op) { |
130 | EUID_ASSERT(); | 147 | EUID_ASSERT(); |
131 | assert(str); | 148 | assert(str); |
132 | 149 | ||
@@ -134,11 +151,13 @@ void env_store(const char *str) { | |||
134 | if (*str == '\0') | 151 | if (*str == '\0') |
135 | goto errexit; | 152 | goto errexit; |
136 | char *ptr = strchr(str, '='); | 153 | char *ptr = strchr(str, '='); |
137 | if (!ptr) | 154 | if (op == SETENV) { |
138 | goto errexit; | 155 | if (!ptr) |
139 | ptr++; | 156 | goto errexit; |
140 | if (*ptr == '\0') | 157 | ptr++; |
141 | goto errexit; | 158 | if (*ptr == '\0') |
159 | goto errexit; | ||
160 | } | ||
142 | 161 | ||
143 | // build list entry | 162 | // build list entry |
144 | Env *env = malloc(sizeof(Env)); | 163 | Env *env = malloc(sizeof(Env)); |
@@ -148,10 +167,13 @@ void env_store(const char *str) { | |||
148 | env->name = strdup(str); | 167 | env->name = strdup(str); |
149 | if (env->name == NULL) | 168 | if (env->name == NULL) |
150 | errExit("strdup"); | 169 | errExit("strdup"); |
151 | char *ptr2 = strchr(env->name, '='); | 170 | if (op == SETENV) { |
152 | assert(ptr2); | 171 | char *ptr2 = strchr(env->name, '='); |
153 | *ptr2 = '\0'; | 172 | assert(ptr2); |
154 | env->value = ptr2 + 1; | 173 | *ptr2 = '\0'; |
174 | env->value = ptr2 + 1; | ||
175 | } | ||
176 | env->op = op; | ||
155 | 177 | ||
156 | // add entry to the list | 178 | // add entry to the list |
157 | env_add(env); | 179 | env_add(env); |
@@ -167,8 +189,13 @@ void env_apply(void) { | |||
167 | Env *env = envlist; | 189 | Env *env = envlist; |
168 | 190 | ||
169 | while (env) { | 191 | while (env) { |
170 | if (setenv(env->name, env->value, 1) < 0) | 192 | if (env->op == SETENV) { |
171 | errExit("setenv"); | 193 | if (setenv(env->name, env->value, 1) < 0) |
194 | errExit("setenv"); | ||
195 | } | ||
196 | else if (env->op == RMENV) { | ||
197 | unsetenv(env->name); | ||
198 | } | ||
172 | env = env->next; | 199 | env = env->next; |
173 | } | 200 | } |
174 | } | 201 | } |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 24ea53476..d7ba539e6 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -22,10 +22,13 @@ | |||
22 | #include "../include/common.h" | 22 | #include "../include/common.h" |
23 | #include "../include/euid_common.h" | 23 | #include "../include/euid_common.h" |
24 | 24 | ||
25 | // debug restricted shell | ||
26 | //#define DEBUG_RESTRICTED_SHELL | ||
25 | 27 | ||
26 | // filesystem | 28 | // filesystem |
27 | #define RUN_FIREJAIL_BASEDIR "/run" | 29 | #define RUN_FIREJAIL_BASEDIR "/run" |
28 | #define RUN_FIREJAIL_DIR "/run/firejail" | 30 | #define RUN_FIREJAIL_DIR "/run/firejail" |
31 | #define RUN_FIREJAIL_APPIMAGE_DIR "/run/firejail/appimage" | ||
29 | #define RUN_FIREJAIL_NAME_DIR "/run/firejail/name" | 32 | #define RUN_FIREJAIL_NAME_DIR "/run/firejail/name" |
30 | #define RUN_FIREJAIL_X11_DIR "/run/firejail/x11" | 33 | #define RUN_FIREJAIL_X11_DIR "/run/firejail/x11" |
31 | #define RUN_FIREJAIL_NETWORK_DIR "/run/firejail/network" | 34 | #define RUN_FIREJAIL_NETWORK_DIR "/run/firejail/network" |
@@ -34,7 +37,6 @@ | |||
34 | #define RUN_RO_DIR "/run/firejail/firejail.ro.dir" | 37 | #define RUN_RO_DIR "/run/firejail/firejail.ro.dir" |
35 | #define RUN_RO_FILE "/run/firejail/firejail.ro.file" | 38 | #define RUN_RO_FILE "/run/firejail/firejail.ro.file" |
36 | #define RUN_MNT_DIR "/run/firejail/mnt" // a tmpfs is mounted on this directory before any of the files below are created | 39 | #define RUN_MNT_DIR "/run/firejail/mnt" // a tmpfs is mounted on this directory before any of the files below are created |
37 | #define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" | ||
38 | #define RUN_CGROUP_CFG "/run/firejail/mnt/cgroup" | 40 | #define RUN_CGROUP_CFG "/run/firejail/mnt/cgroup" |
39 | #define RUN_CPU_CFG "/run/firejail/mnt/cpu" | 41 | #define RUN_CPU_CFG "/run/firejail/mnt/cpu" |
40 | #define RUN_GROUPS_CFG "/run/firejail/mnt/groups" | 42 | #define RUN_GROUPS_CFG "/run/firejail/mnt/groups" |
@@ -43,8 +45,15 @@ | |||
43 | #define RUN_HOME_DIR "/run/firejail/mnt/home" | 45 | #define RUN_HOME_DIR "/run/firejail/mnt/home" |
44 | #define RUN_ETC_DIR "/run/firejail/mnt/etc" | 46 | #define RUN_ETC_DIR "/run/firejail/mnt/etc" |
45 | #define RUN_BIN_DIR "/run/firejail/mnt/bin" | 47 | #define RUN_BIN_DIR "/run/firejail/mnt/bin" |
46 | #define RUN_DRI_DIR "/run/firejail/mnt/dri" | ||
47 | #define RUN_PULSE_DIR "/run/firejail/mnt/pulse" | 48 | #define RUN_PULSE_DIR "/run/firejail/mnt/pulse" |
49 | |||
50 | #define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" // configured filter | ||
51 | #define RUN_SECCOMP_PROTOCOL "/run/firejail/mnt/seccomp.protocol" // protocol filter | ||
52 | #define RUN_SECCOMP_AMD64 "/run/firejail/mnt/seccomp.amd64" // amd64 filter installed on i386 architectures | ||
53 | #define RUN_SECCOMP_I386 "/run/firejail/mnt/seccomp.i386" // i386 filter installed on amd64 architectures | ||
54 | |||
55 | |||
56 | #define RUN_DEV_DIR "/run/firejail/mnt/dev" | ||
48 | #define RUN_DEVLOG_FILE "/run/firejail/mnt/devlog" | 57 | #define RUN_DEVLOG_FILE "/run/firejail/mnt/devlog" |
49 | 58 | ||
50 | #define RUN_WHITELIST_X11_DIR "/run/firejail/mnt/orig-x11" | 59 | #define RUN_WHITELIST_X11_DIR "/run/firejail/mnt/orig-x11" |
@@ -52,11 +61,14 @@ | |||
52 | #define RUN_WHITELIST_HOME_USER_DIR "/run/firejail/mnt/orig-home-user" // home directory whitelisting | 61 | #define RUN_WHITELIST_HOME_USER_DIR "/run/firejail/mnt/orig-home-user" // home directory whitelisting |
53 | #define RUN_WHITELIST_TMP_DIR "/run/firejail/mnt/orig-tmp" | 62 | #define RUN_WHITELIST_TMP_DIR "/run/firejail/mnt/orig-tmp" |
54 | #define RUN_WHITELIST_MEDIA_DIR "/run/firejail/mnt/orig-media" | 63 | #define RUN_WHITELIST_MEDIA_DIR "/run/firejail/mnt/orig-media" |
64 | #define RUN_WHITELIST_MNT_DIR "/run/firejail/mnt/orig-mnt" | ||
55 | #define RUN_WHITELIST_VAR_DIR "/run/firejail/mnt/orig-var" | 65 | #define RUN_WHITELIST_VAR_DIR "/run/firejail/mnt/orig-var" |
56 | #define RUN_WHITELIST_DEV_DIR "/run/firejail/mnt/orig-dev" | 66 | #define RUN_WHITELIST_DEV_DIR "/run/firejail/mnt/orig-dev" |
57 | #define RUN_WHITELIST_OPT_DIR "/run/firejail/mnt/orig-opt" | 67 | #define RUN_WHITELIST_OPT_DIR "/run/firejail/mnt/orig-opt" |
68 | #define RUN_WHITELIST_SRV_DIR "/run/firejail/mnt/orig-srv" | ||
58 | 69 | ||
59 | #define RUN_XAUTHORITY_FILE "/run/firejail/mnt/.Xauthority" | 70 | #define RUN_XAUTHORITY_FILE "/run/firejail/mnt/.Xauthority" |
71 | #define RUN_XAUTHORITY_SEC_FILE "/run/firejail/mnt/sec.Xauthority" | ||
60 | #define RUN_ASOUNDRC_FILE "/run/firejail/mnt/.asoundrc" | 72 | #define RUN_ASOUNDRC_FILE "/run/firejail/mnt/.asoundrc" |
61 | #define RUN_HOSTNAME_FILE "/run/firejail/mnt/hostname" | 73 | #define RUN_HOSTNAME_FILE "/run/firejail/mnt/hostname" |
62 | #define RUN_HOSTS_FILE "/run/firejail/mnt/hosts" | 74 | #define RUN_HOSTS_FILE "/run/firejail/mnt/hosts" |
@@ -67,11 +79,59 @@ | |||
67 | #define RUN_GROUP_FILE "/run/firejail/mnt/group" | 79 | #define RUN_GROUP_FILE "/run/firejail/mnt/group" |
68 | #define RUN_FSLOGGER_FILE "/run/firejail/mnt/fslogger" | 80 | #define RUN_FSLOGGER_FILE "/run/firejail/mnt/fslogger" |
69 | 81 | ||
82 | |||
83 | |||
70 | // profiles | 84 | // profiles |
71 | #define DEFAULT_USER_PROFILE "generic" | 85 | #define DEFAULT_USER_PROFILE "default" |
72 | #define DEFAULT_ROOT_PROFILE "server" | 86 | #define DEFAULT_ROOT_PROFILE "server" |
73 | #define MAX_INCLUDE_LEVEL 6 // include levels in profile files | 87 | #define MAX_INCLUDE_LEVEL 6 // include levels in profile files |
74 | 88 | ||
89 | |||
90 | #define ASSERT_PERMS(file, uid, gid, mode) \ | ||
91 | do { \ | ||
92 | assert(file);\ | ||
93 | struct stat s;\ | ||
94 | if (stat(file, &s) == -1) errExit("stat");\ | ||
95 | assert(s.st_uid == uid);\ | ||
96 | assert(s.st_gid == gid);\ | ||
97 | assert((s.st_mode & 07777) == (mode));\ | ||
98 | } while (0) | ||
99 | #define ASSERT_PERMS_FD(fd, uid, gid, mode) \ | ||
100 | do { \ | ||
101 | struct stat s;\ | ||
102 | if (stat(fd, &s) == -1) errExit("stat");\ | ||
103 | assert(s.st_uid == uid);\ | ||
104 | assert(s.st_gid == gid);\ | ||
105 | assert((s.st_mode & 07777) == (mode));\ | ||
106 | } while (0) | ||
107 | #define ASSERT_PERMS_STREAM(file, uid, gid, mode) \ | ||
108 | do { \ | ||
109 | int fd = fileno(file);\ | ||
110 | if (fd == -1) errExit("fileno");\ | ||
111 | ASSERT_PERMS_FD(fd, uid, gid, (mode));\ | ||
112 | } while (0) | ||
113 | |||
114 | #define SET_PERMS_FD(fd, uid, gid, mode) \ | ||
115 | do { \ | ||
116 | if (fchmod(fd, (mode)) == -1) errExit("chmod");\ | ||
117 | if (fchown(fd, uid, gid) == -1) errExit("chown");\ | ||
118 | } while (0) | ||
119 | #define SET_PERMS_STREAM(stream, uid, gid, mode) \ | ||
120 | do { \ | ||
121 | int fd = fileno(stream);\ | ||
122 | if (fd == -1) errExit("fileno");\ | ||
123 | SET_PERMS_FD(fd, uid, gid, (mode));\ | ||
124 | } while (0) | ||
125 | #define SET_PERMS_STREAM_NOERR(stream, uid, gid, mode) \ | ||
126 | do { \ | ||
127 | int fd = fileno(stream);\ | ||
128 | if (fd == -1) continue;\ | ||
129 | int rv = fchmod(fd, (mode));\ | ||
130 | (void) rv;\ | ||
131 | rv = fchown(fd, uid, gid);\ | ||
132 | (void) rv;\ | ||
133 | } while (0) | ||
134 | |||
75 | // main.c | 135 | // main.c |
76 | typedef struct bridge_t { | 136 | typedef struct bridge_t { |
77 | // on the host | 137 | // on the host |
@@ -81,6 +141,8 @@ typedef struct bridge_t { | |||
81 | uint8_t mac[6]; // interface mac address | 141 | uint8_t mac[6]; // interface mac address |
82 | int mtu; // interface mtu | 142 | int mtu; // interface mtu |
83 | 143 | ||
144 | char *veth_name; // veth name for the device connected to the bridge | ||
145 | |||
84 | // inside the sandbox | 146 | // inside the sandbox |
85 | char *devsandbox; // name of the device inside the sandbox | 147 | char *devsandbox; // name of the device inside the sandbox |
86 | uint32_t ipsandbox; // ip address inside the sandbox | 148 | uint32_t ipsandbox; // ip address inside the sandbox |
@@ -115,9 +177,11 @@ typedef struct profile_entry_t { | |||
115 | unsigned home_dir:1; // whitelist in /home/user directory | 177 | unsigned home_dir:1; // whitelist in /home/user directory |
116 | unsigned tmp_dir:1; // whitelist in /tmp directory | 178 | unsigned tmp_dir:1; // whitelist in /tmp directory |
117 | unsigned media_dir:1; // whitelist in /media directory | 179 | unsigned media_dir:1; // whitelist in /media directory |
180 | unsigned mnt_dir:1; // whitelist in /mnt directory | ||
118 | unsigned var_dir:1; // whitelist in /var directory | 181 | unsigned var_dir:1; // whitelist in /var directory |
119 | unsigned dev_dir:1; // whitelist in /dev directory | 182 | unsigned dev_dir:1; // whitelist in /dev directory |
120 | unsigned opt_dir:1; // whitelist in /opt directory | 183 | unsigned opt_dir:1; // whitelist in /opt directory |
184 | unsigned srv_dir:1; // whitelist in /srv directory | ||
121 | }ProfileEntry; | 185 | }ProfileEntry; |
122 | 186 | ||
123 | typedef struct config_t { | 187 | typedef struct config_t { |
@@ -131,10 +195,12 @@ typedef struct config_t { | |||
131 | char *profile_ignore[MAX_PROFILE_IGNORE]; | 195 | char *profile_ignore[MAX_PROFILE_IGNORE]; |
132 | char *chrootdir; // chroot directory | 196 | char *chrootdir; // chroot directory |
133 | char *home_private; // private home directory | 197 | char *home_private; // private home directory |
198 | char *home_private_keep; // keep list for private home directory | ||
134 | char *etc_private_keep; // keep list for private etc directory | 199 | char *etc_private_keep; // keep list for private etc directory |
135 | char *bin_private_keep; // keep list for private bin directory | 200 | char *bin_private_keep; // keep list for private bin directory |
136 | char *cwd; // current working directory | 201 | char *cwd; // current working directory |
137 | char *overlay_dir; | 202 | char *overlay_dir; |
203 | char *private_template; // template dir for tmpfs home | ||
138 | 204 | ||
139 | // networking | 205 | // networking |
140 | char *name; // sandbox name | 206 | char *name; // sandbox name |
@@ -156,7 +222,6 @@ typedef struct config_t { | |||
156 | char *seccomp_list;// optional seccomp list on top of default filter | 222 | char *seccomp_list;// optional seccomp list on top of default filter |
157 | char *seccomp_list_drop; // seccomp drop list | 223 | char *seccomp_list_drop; // seccomp drop list |
158 | char *seccomp_list_keep; // seccomp keep list | 224 | char *seccomp_list_keep; // seccomp keep list |
159 | char **seccomp_list_errno; // seccomp errno[nr] lists | ||
160 | char *protocol; // protocol list | 225 | char *protocol; // protocol list |
161 | 226 | ||
162 | // rlimits | 227 | // rlimits |
@@ -211,6 +276,7 @@ static inline int any_interface_configured(void) { | |||
211 | void clear_run_files(pid_t pid); | 276 | void clear_run_files(pid_t pid); |
212 | 277 | ||
213 | extern int arg_private; // mount private /home | 278 | extern int arg_private; // mount private /home |
279 | extern int arg_private_template; // private /home template | ||
214 | extern int arg_debug; // print debug messages | 280 | extern int arg_debug; // print debug messages |
215 | extern int arg_debug_check_filename; // print debug messages for filename checking | 281 | extern int arg_debug_check_filename; // print debug messages for filename checking |
216 | extern int arg_debug_blacklists; // print debug messages for blacklists | 282 | extern int arg_debug_blacklists; // print debug messages for blacklists |
@@ -218,9 +284,8 @@ extern int arg_debug_whitelists; // print debug messages for whitelists | |||
218 | extern int arg_nonetwork; // --net=none | 284 | extern int arg_nonetwork; // --net=none |
219 | extern int arg_command; // -c | 285 | extern int arg_command; // -c |
220 | extern int arg_overlay; // overlay option | 286 | extern int arg_overlay; // overlay option |
221 | extern int arg_overlay_keep; // place overlay diff directory in ~/.firejail | 287 | extern int arg_overlay_keep; // place overlay diff in a known directory |
222 | extern int arg_zsh; // use zsh as default shell | 288 | extern int arg_overlay_reuse; // allow the reuse of overlays |
223 | extern int arg_csh; // use csh as default shell | ||
224 | 289 | ||
225 | extern int arg_seccomp; // enable default seccomp filter | 290 | extern int arg_seccomp; // enable default seccomp filter |
226 | 291 | ||
@@ -237,6 +302,7 @@ extern int arg_rlimit_nproc; // rlimit nproc | |||
237 | extern int arg_rlimit_fsize; // rlimit fsize | 302 | extern int arg_rlimit_fsize; // rlimit fsize |
238 | extern int arg_rlimit_sigpending;// rlimit sigpending | 303 | extern int arg_rlimit_sigpending;// rlimit sigpending |
239 | extern int arg_nogroups; // disable supplementary groups | 304 | extern int arg_nogroups; // disable supplementary groups |
305 | extern int arg_nonewprivs; // set the NO_NEW_PRIVS prctl | ||
240 | extern int arg_noroot; // create a new user namespace and disable root user | 306 | extern int arg_noroot; // create a new user namespace and disable root user |
241 | extern int arg_netfilter; // enable netfilter | 307 | extern int arg_netfilter; // enable netfilter |
242 | extern int arg_netfilter6; // enable netfilter6 | 308 | extern int arg_netfilter6; // enable netfilter6 |
@@ -251,12 +317,24 @@ extern int arg_private_tmp; // private tmp directory | |||
251 | extern int arg_scan; // arp-scan all interfaces | 317 | extern int arg_scan; // arp-scan all interfaces |
252 | extern int arg_whitelist; // whitelist commad | 318 | extern int arg_whitelist; // whitelist commad |
253 | extern int arg_nosound; // disable sound | 319 | extern int arg_nosound; // disable sound |
320 | extern int arg_no3d; // disable 3d hardware acceleration | ||
254 | extern int arg_quiet; // no output for scripting | 321 | extern int arg_quiet; // no output for scripting |
255 | extern int arg_join_network; // join only the network namespace | 322 | extern int arg_join_network; // join only the network namespace |
256 | extern int arg_join_filesystem; // join only the mount namespace | 323 | extern int arg_join_filesystem; // join only the mount namespace |
257 | extern int arg_nice; // nice value configured | 324 | extern int arg_nice; // nice value configured |
258 | extern int arg_ipc; // enable ipc namespace | 325 | extern int arg_ipc; // enable ipc namespace |
259 | 326 | extern int arg_writable_etc; // writable etc | |
327 | extern int arg_writable_var; // writable var | ||
328 | extern int arg_appimage; // appimage | ||
329 | extern int arg_audit; // audit | ||
330 | extern char *arg_audit_prog; // audit | ||
331 | extern int arg_apparmor; // apparmor | ||
332 | extern int arg_allow_debuggers; // allow debuggers | ||
333 | extern int arg_x11_block; // block X11 | ||
334 | extern int arg_x11_xorg; // use X11 security extention | ||
335 | extern int arg_allusers; // all user home directories visible | ||
336 | |||
337 | extern int login_shell; | ||
260 | extern int parent_to_child_fds[2]; | 338 | extern int parent_to_child_fds[2]; |
261 | extern int child_to_parent_fds[2]; | 339 | extern int child_to_parent_fds[2]; |
262 | extern pid_t sandbox_pid; | 340 | extern pid_t sandbox_pid; |
@@ -267,16 +345,17 @@ extern int fullargc; | |||
267 | 345 | ||
268 | // main.c | 346 | // main.c |
269 | void check_user_namespace(void); | 347 | void check_user_namespace(void); |
348 | char *guess_shell(void); | ||
270 | 349 | ||
271 | // sandbox.c | 350 | // sandbox.c |
272 | int sandbox(void* sandbox_arg); | 351 | int sandbox(void* sandbox_arg); |
352 | void start_application(void); | ||
273 | 353 | ||
274 | // network_main.c | 354 | // network_main.c |
275 | void net_configure_bridge(Bridge *br, char *dev_name); | 355 | void net_configure_bridge(Bridge *br, char *dev_name); |
276 | void net_configure_sandbox_ip(Bridge *br); | 356 | void net_configure_sandbox_ip(Bridge *br); |
277 | void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child); | 357 | void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child); |
278 | void net_check_cfg(void); | 358 | void net_check_cfg(void); |
279 | void net_dns_print_name(const char *name); | ||
280 | void net_dns_print(pid_t pid); | 359 | void net_dns_print(pid_t pid); |
281 | void network_main(pid_t child); | 360 | void network_main(pid_t child); |
282 | 361 | ||
@@ -287,35 +366,35 @@ void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); | |||
287 | void net_if_ip6(const char *ifname, const char *addr6); | 366 | void net_if_ip6(const char *ifname, const char *addr6); |
288 | int net_get_if_addr(const char *bridge, uint32_t *ip, uint32_t *mask, uint8_t mac[6], int *mtu); | 367 | int net_get_if_addr(const char *bridge, uint32_t *ip, uint32_t *mask, uint8_t mac[6], int *mtu); |
289 | int net_add_route(uint32_t dest, uint32_t mask, uint32_t gw); | 368 | int net_add_route(uint32_t dest, uint32_t mask, uint32_t gw); |
290 | void net_ifprint(void); | ||
291 | void net_bridge_add_interface(const char *bridge, const char *dev); | ||
292 | uint32_t network_get_defaultgw(void); | 369 | uint32_t network_get_defaultgw(void); |
293 | int net_config_mac(const char *ifname, const unsigned char mac[6]); | 370 | int net_config_mac(const char *ifname, const unsigned char mac[6]); |
294 | int net_get_mac(const char *ifname, unsigned char mac[6]); | 371 | int net_get_mac(const char *ifname, unsigned char mac[6]); |
372 | void net_config_interface(const char *dev, uint32_t ip, uint32_t mask, int mtu); | ||
373 | |||
374 | // preproc.c | ||
375 | void preproc_build_firejail_dir(void); | ||
376 | void preproc_mount_mnt_dir(void); | ||
377 | void preproc_build_cp_command(void); | ||
378 | void preproc_delete_cp_command(void) ; | ||
379 | void preproc_remount_mnt_dir(void); | ||
295 | 380 | ||
296 | // fs.c | 381 | // fs.c |
297 | // build /run/firejail directory | ||
298 | void fs_build_firejail_dir(void); | ||
299 | // build /run/firejail/mnt directory | ||
300 | void fs_build_mnt_dir(void); | ||
301 | // grab a copy of cp command | ||
302 | void fs_build_cp_command(void); | ||
303 | // delete the temporary cp command | ||
304 | void fs_delete_cp_command(void) ; | ||
305 | // blacklist files or directoies by mounting empty files on top of them | 382 | // blacklist files or directoies by mounting empty files on top of them |
306 | void fs_blacklist(void); | 383 | void fs_blacklist(void); |
307 | // remount a directory read-only | 384 | // remount a directory read-only |
308 | void fs_rdonly(const char *dir); | 385 | void fs_rdonly(const char *dir); |
386 | // remount a directory noexec, nodev and nosuid | ||
387 | void fs_noexec(const char *dir); | ||
309 | // mount /proc and /sys directories | 388 | // mount /proc and /sys directories |
310 | void fs_proc_sys_dev_boot(void); | 389 | void fs_proc_sys_dev_boot(void); |
311 | // build a basic read-only filesystem | 390 | // build a basic read-only filesystem |
312 | void fs_basic_fs(void); | 391 | void fs_basic_fs(void); |
313 | // mount overlayfs on top of / directory | 392 | // mount overlayfs on top of / directory |
393 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse); | ||
314 | void fs_overlayfs(void); | 394 | void fs_overlayfs(void); |
315 | // chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf | 395 | // chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf |
316 | void fs_chroot(const char *rootdir); | 396 | void fs_chroot(const char *rootdir); |
317 | int fs_check_chroot_dir(const char *rootdir); | 397 | int fs_check_chroot_dir(const char *rootdir); |
318 | void fs_private_tmp(void); | ||
319 | 398 | ||
320 | // profile.c | 399 | // profile.c |
321 | // find and read the profile specified by name from dir directory | 400 | // find and read the profile specified by name from dir directory |
@@ -340,12 +419,11 @@ void usage(void); | |||
340 | 419 | ||
341 | // join.c | 420 | // join.c |
342 | void join(pid_t pid, int argc, char **argv, int index); | 421 | void join(pid_t pid, int argc, char **argv, int index); |
343 | void join_name(const char *name, int argc, char **argv, int index); | 422 | |
423 | // shutdown.c | ||
344 | void shut(pid_t pid); | 424 | void shut(pid_t pid); |
345 | void shut_name(const char *name); | ||
346 | 425 | ||
347 | // restricted_shell.c | 426 | // restricted_shell.c |
348 | extern char *restricted_user; | ||
349 | int restricted_shell(const char *user); | 427 | int restricted_shell(const char *user); |
350 | 428 | ||
351 | // arp.c | 429 | // arp.c |
@@ -353,13 +431,6 @@ int restricted_shell(const char *user); | |||
353 | int arp_check(const char *dev, uint32_t destaddr, uint32_t srcaddr); | 431 | int arp_check(const char *dev, uint32_t destaddr, uint32_t srcaddr); |
354 | // assign an IP address using arp scanning | 432 | // assign an IP address using arp scanning |
355 | uint32_t arp_assign(const char *dev, Bridge *br); | 433 | uint32_t arp_assign(const char *dev, Bridge *br); |
356 | // scan interface (--scan option) | ||
357 | void arp_scan(const char *dev, uint32_t srcaddr, uint32_t srcmask); | ||
358 | |||
359 | // veth.c | ||
360 | int net_create_veth(const char *dev, const char *nsdev, unsigned pid); | ||
361 | int net_create_macvlan(const char *dev, const char *parent, unsigned pid); | ||
362 | int net_move_interface(const char *dev, unsigned pid); | ||
363 | 434 | ||
364 | // util.c | 435 | // util.c |
365 | void drop_privs(int nogroups); | 436 | void drop_privs(int nogroups); |
@@ -369,7 +440,7 @@ void logsignal(int s); | |||
369 | void logmsg(const char *msg); | 440 | void logmsg(const char *msg); |
370 | void logargs(int argc, char **argv) ; | 441 | void logargs(int argc, char **argv) ; |
371 | void logerr(const char *msg); | 442 | void logerr(const char *msg); |
372 | int copy_file(const char *srcname, const char *destname); | 443 | int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); |
373 | int is_dir(const char *fname); | 444 | int is_dir(const char *fname); |
374 | int is_link(const char *fname); | 445 | int is_link(const char *fname); |
375 | char *line_remove_spaces(const char *buf); | 446 | char *line_remove_spaces(const char *buf); |
@@ -384,8 +455,13 @@ char *expand_home(const char *path, const char* homedir); | |||
384 | const char *gnu_basename(const char *path); | 455 | const char *gnu_basename(const char *path); |
385 | uid_t pid_get_uid(pid_t pid); | 456 | uid_t pid_get_uid(pid_t pid); |
386 | void invalid_filename(const char *fname); | 457 | void invalid_filename(const char *fname); |
387 | uid_t get_tty_gid(void); | 458 | uid_t get_group_id(const char *group); |
388 | uid_t get_audio_gid(void); | 459 | int remove_directory(const char *path); |
460 | void flush_stdin(void); | ||
461 | void create_empty_dir_as_root(const char *dir, mode_t mode); | ||
462 | void create_empty_file_as_root(const char *dir, mode_t mode); | ||
463 | int set_perms(const char *fname, uid_t uid, gid_t gid, mode_t mode); | ||
464 | void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid); | ||
389 | 465 | ||
390 | // fs_var.c | 466 | // fs_var.c |
391 | void fs_var_log(void); // mounting /var/log | 467 | void fs_var_log(void); // mounting /var/log |
@@ -400,23 +476,33 @@ void dbg_test_dir(const char *dir); | |||
400 | // fs_dev.c | 476 | // fs_dev.c |
401 | void fs_dev_shm(void); | 477 | void fs_dev_shm(void); |
402 | void fs_private_dev(void); | 478 | void fs_private_dev(void); |
479 | void fs_dev_disable_sound(void); | ||
480 | void fs_dev_disable_3d(void); | ||
403 | 481 | ||
404 | // fs_home.c | 482 | // fs_home.c |
405 | // private mode (--private) | 483 | // private mode (--private) |
406 | void fs_private(void); | 484 | void fs_private(void); |
407 | // private mode (--private=homedir) | 485 | // private mode (--private=homedir) |
408 | void fs_private_homedir(void); | 486 | void fs_private_homedir(void); |
487 | // private template (--private-template=templatedir) | ||
488 | void fs_private_template(void); | ||
409 | // check new private home directory (--private= option) - exit if it fails | 489 | // check new private home directory (--private= option) - exit if it fails |
410 | void fs_check_private_dir(void); | 490 | void fs_check_private_dir(void); |
491 | // check new private template home directory (--private-template= option) exit if it fails | ||
492 | void fs_check_private_template(void); | ||
493 | // check directory list specified by user (--private-home option) - exit if it fails | ||
494 | void fs_check_home_list(void); | ||
495 | void fs_private_home_list(void); | ||
411 | 496 | ||
412 | 497 | ||
413 | // seccomp.c | 498 | // seccomp.c |
499 | char *seccomp_check_list(const char *str); | ||
500 | int seccomp_load(const char *fname); | ||
501 | void seccomp_filter_32(void); | ||
502 | void seccomp_filter_64(void); | ||
414 | int seccomp_filter_drop(int enforce_seccomp); | 503 | int seccomp_filter_drop(int enforce_seccomp); |
415 | int seccomp_filter_keep(void); | 504 | int seccomp_filter_keep(void); |
416 | void seccomp_set(void); | ||
417 | void seccomp_print_filter_name(const char *name); | ||
418 | void seccomp_print_filter(pid_t pid); | 505 | void seccomp_print_filter(pid_t pid); |
419 | int seccomp_filter_errno(void); | ||
420 | 506 | ||
421 | // caps.c | 507 | // caps.c |
422 | int caps_default_filter(void); | 508 | int caps_default_filter(void); |
@@ -427,14 +513,11 @@ int caps_check_list(const char *clist, void (*callback)(int)); | |||
427 | void caps_drop_list(const char *clist); | 513 | void caps_drop_list(const char *clist); |
428 | void caps_keep_list(const char *clist); | 514 | void caps_keep_list(const char *clist); |
429 | void caps_print_filter(pid_t pid); | 515 | void caps_print_filter(pid_t pid); |
430 | void caps_print_filter_name(const char *name); | ||
431 | 516 | ||
432 | // syscall.c | 517 | // syscall.c |
433 | const char *syscall_find_nr(int nr); | 518 | const char *syscall_find_nr(int nr); |
434 | // return -1 if error, 0 if no error | 519 | // return -1 if error, 0 if no error |
435 | int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg), int arg); | 520 | int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg), int arg); |
436 | // print all available syscallsseccomp | ||
437 | void syscall_print(void); | ||
438 | 521 | ||
439 | // fs_trace.c | 522 | // fs_trace.c |
440 | void fs_trace_preload(void); | 523 | void fs_trace_preload(void); |
@@ -452,7 +535,6 @@ void read_cpu_list(const char *str); | |||
452 | void set_cpu_affinity(void); | 535 | void set_cpu_affinity(void); |
453 | void load_cpu(const char *fname); | 536 | void load_cpu(const char *fname); |
454 | void save_cpu(void); | 537 | void save_cpu(void); |
455 | void cpu_print_filter_name(const char *name); | ||
456 | void cpu_print_filter(pid_t pid); | 538 | void cpu_print_filter(pid_t pid); |
457 | 539 | ||
458 | // cgroup.c | 540 | // cgroup.c |
@@ -470,7 +552,6 @@ void netfilter6(const char *fname); | |||
470 | 552 | ||
471 | // bandwidth.c | 553 | // bandwidth.c |
472 | void bandwidth_del_run_file(pid_t pid); | 554 | void bandwidth_del_run_file(pid_t pid); |
473 | void bandwidth_name(const char *name, const char *command, const char *dev, int down, int up); | ||
474 | void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up); | 555 | void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up); |
475 | void network_del_run_file(pid_t pid); | 556 | void network_del_run_file(pid_t pid); |
476 | void network_set_run_file(pid_t pid); | 557 | void network_set_run_file(pid_t pid); |
@@ -480,11 +561,17 @@ void fs_check_etc_list(void); | |||
480 | void fs_private_etc_list(void); | 561 | void fs_private_etc_list(void); |
481 | 562 | ||
482 | // no_sandbox.c | 563 | // no_sandbox.c |
564 | int check_namespace_virt(void); | ||
483 | int check_kernel_procs(void); | 565 | int check_kernel_procs(void); |
484 | void run_no_sandbox(int argc, char **argv); | 566 | void run_no_sandbox(int argc, char **argv); |
485 | 567 | ||
486 | // env.c | 568 | // env.c |
487 | void env_store(const char *str); | 569 | typedef enum { |
570 | SETENV = 0, | ||
571 | RMENV | ||
572 | } ENV_OP; | ||
573 | |||
574 | void env_store(const char *str, ENV_OP op); | ||
488 | void env_apply(void); | 575 | void env_apply(void); |
489 | void env_defaults(void); | 576 | void env_defaults(void); |
490 | void env_ibus_load(void); | 577 | void env_ibus_load(void); |
@@ -507,13 +594,9 @@ void fs_check_bin_list(void); | |||
507 | void fs_private_bin_list(void); | 594 | void fs_private_bin_list(void); |
508 | 595 | ||
509 | // protocol.c | 596 | // protocol.c |
510 | void protocol_list(); | ||
511 | void protocol_print_filter_name(const char *name); | ||
512 | void protocol_print_filter(pid_t pid); | ||
513 | void protocol_store(const char *prlist); | ||
514 | void protocol_filter(void); | ||
515 | void protocol_filter_save(void); | 597 | void protocol_filter_save(void); |
516 | void protocol_filter_load(const char *fname); | 598 | void protocol_filter_load(const char *fname); |
599 | void protocol_print_filter(pid_t pid); | ||
517 | 600 | ||
518 | // restrict_users.c | 601 | // restrict_users.c |
519 | void restrict_users(void); | 602 | void restrict_users(void); |
@@ -525,46 +608,90 @@ void fs_logger2int(const char *msg1, int d); | |||
525 | void fs_logger3(const char *msg1, const char *msg2, const char *msg3); | 608 | void fs_logger3(const char *msg1, const char *msg2, const char *msg3); |
526 | void fs_logger_print(void); | 609 | void fs_logger_print(void); |
527 | void fs_logger_change_owner(void); | 610 | void fs_logger_change_owner(void); |
528 | void fs_logger_print_log_name(const char *name); | ||
529 | void fs_logger_print_log(pid_t pid); | 611 | void fs_logger_print_log(pid_t pid); |
530 | 612 | ||
531 | // run_symlink.c | 613 | // run_symlink.c |
532 | void run_symlink(int argc, char **argv); | 614 | void run_symlink(int argc, char **argv); |
533 | 615 | ||
534 | // user.c | ||
535 | void check_user(int argc, char **argv); | ||
536 | |||
537 | // paths.c | 616 | // paths.c |
538 | char **build_paths(void); | 617 | char **build_paths(void); |
539 | 618 | ||
540 | // fs_mkdir.c | 619 | // fs_mkdir.c |
541 | void fs_mkdir(const char *name); | 620 | void fs_mkdir(const char *name); |
621 | void fs_mkfile(const char *name); | ||
542 | 622 | ||
543 | // x11.c | 623 | // x11.c |
624 | extern int mask_x11_abstract_socket; | ||
544 | void fs_x11(void); | 625 | void fs_x11(void); |
545 | int x11_display(void); | 626 | int x11_display(void); |
546 | void x11_start(int argc, char **argv); | 627 | void x11_start(int argc, char **argv); |
547 | void x11_start_xpra(int argc, char **argv); | 628 | void x11_start_xpra(int argc, char **argv); |
548 | void x11_start_xephyr(int argc, char **argv); | 629 | void x11_start_xephyr(int argc, char **argv); |
549 | extern char *xephyr_screen; | 630 | void x11_block(void); |
550 | 631 | ||
551 | // ls.c | 632 | // ls.c |
552 | #define SANDBOX_FS_LS 0 | 633 | enum { |
553 | #define SANDBOX_FS_GET 1 | 634 | SANDBOX_FS_LS = 0, |
554 | void sandboxfs_name(int op, const char *name, const char *path); | 635 | SANDBOX_FS_GET, |
555 | void sandboxfs(int op, pid_t pid, const char *patqh); | 636 | SANDBOX_FS_PUT, |
637 | SANDBOX_FS_MAX // this should always be the last entry | ||
638 | }; | ||
639 | void sandboxfs(int op, pid_t pid, const char *path1, const char *path2); | ||
556 | 640 | ||
557 | // checkcfg.c | 641 | // checkcfg.c |
558 | #define CFG_FILE_TRANSFER 0 | 642 | enum { |
559 | #define CFG_X11 1 | 643 | CFG_FILE_TRANSFER = 0, |
560 | #define CFG_BIND 2 | 644 | CFG_X11, |
561 | #define CFG_USERNS 3 | 645 | CFG_BIND, |
562 | #define CFG_CHROOT 4 | 646 | CFG_USERNS, |
563 | #define CFG_SECCOMP 5 | 647 | CFG_CHROOT, |
564 | #define CFG_NETWORK 6 | 648 | CFG_SECCOMP, |
565 | #define CFG_RESTRICTED_NETWORK 7 | 649 | CFG_NETWORK, |
566 | #define CFG_MAX 8 // this should always be the last entry | 650 | CFG_RESTRICTED_NETWORK, |
651 | CFG_FORCE_NONEWPRIVS, | ||
652 | CFG_WHITELIST, | ||
653 | CFG_XEPHYR_WINDOW_TITLE, | ||
654 | CFG_REMOUNT_PROC_SYS, | ||
655 | CFG_OVERLAYFS, | ||
656 | CFG_CHROOT_DESKTOP, | ||
657 | CFG_PRIVATE_HOME, | ||
658 | CFG_PRIVATE_BIN_NO_LOCAL, | ||
659 | CFG_MAX // this should always be the last entry | ||
660 | }; | ||
661 | extern char *xephyr_screen; | ||
662 | extern char *xephyr_extra_params; | ||
663 | extern char *netfilter_default; | ||
567 | int checkcfg(int val); | 664 | int checkcfg(int val); |
665 | void print_compiletime_support(void); | ||
666 | void x11_xorg(void); | ||
667 | |||
668 | // appimage.c | ||
669 | void appimage_set(const char *appimage_path); | ||
670 | void appimage_clear(void); | ||
671 | const char *appimage_getdir(void); | ||
672 | |||
673 | // appimage_size.c | ||
674 | long unsigned int appimage2_size(const char *fname); | ||
675 | |||
676 | // cmdline.c | ||
677 | void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); | ||
678 | |||
679 | // sbox.c | ||
680 | // programs | ||
681 | #define PATH_FNET (LIBDIR "/firejail/fnet") | ||
682 | #define PATH_FIREMON (PREFIX "/bin/firemon") | ||
683 | #define PATH_FSECCOMP (LIBDIR "/firejail/fseccomp") | ||
684 | // bitmapped filters for sbox_run | ||
685 | #define SBOX_ROOT (1 << 0) // run the sandbox as root | ||
686 | #define SBOX_USER (1 << 1) // run the sandbox as a regular user | ||
687 | #define SBOX_SECCOMP (1 << 2) // install seccomp | ||
688 | #define SBOX_CAPS_NONE (1 << 3) // drop all capabilities | ||
689 | #define SBOX_CAPS_NETWORK (1 << 4) // caps filter for programs running network programs | ||
690 | #define SBOX_ALLOW_STDIN (1 << 5) // don't close stdin | ||
691 | |||
692 | // run sbox | ||
693 | int sbox_run(unsigned filter, int num, ...); | ||
694 | |||
568 | 695 | ||
569 | #endif | 696 | #endif |
570 | 697 | ||
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 7ee76d096..7ff7e3c59 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -27,198 +27,9 @@ | |||
27 | #include <fcntl.h> | 27 | #include <fcntl.h> |
28 | #include <errno.h> | 28 | #include <errno.h> |
29 | 29 | ||
30 | static void create_empty_dir(void) { | 30 | static void fs_rdwr(const char *dir); |
31 | struct stat s; | ||
32 | |||
33 | if (stat(RUN_RO_DIR, &s)) { | ||
34 | /* coverity[toctou] */ | ||
35 | int rv = mkdir(RUN_RO_DIR, S_IRUSR | S_IXUSR); | ||
36 | if (rv == -1) | ||
37 | errExit("mkdir"); | ||
38 | if (chown(RUN_RO_DIR, 0, 0) < 0) | ||
39 | errExit("chown"); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | static void create_empty_file(void) { | ||
44 | struct stat s; | ||
45 | |||
46 | if (stat(RUN_RO_FILE, &s)) { | ||
47 | /* coverity[toctou] */ | ||
48 | FILE *fp = fopen(RUN_RO_FILE, "w"); | ||
49 | if (!fp) | ||
50 | errExit("fopen"); | ||
51 | fclose(fp); | ||
52 | if (chown(RUN_RO_FILE, 0, 0) < 0) | ||
53 | errExit("chown"); | ||
54 | if (chmod(RUN_RO_FILE, S_IRUSR) < 0) | ||
55 | errExit("chown"); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | // build /run/firejail directory | ||
60 | void fs_build_firejail_dir(void) { | ||
61 | struct stat s; | ||
62 | |||
63 | // CentOS 6 doesn't have /run directory | ||
64 | if (stat(RUN_FIREJAIL_BASEDIR, &s)) { | ||
65 | if (arg_debug) | ||
66 | printf("Creating %s directory\n", RUN_FIREJAIL_BASEDIR); | ||
67 | /* coverity[toctou] */ | ||
68 | int rv = mkdir(RUN_FIREJAIL_BASEDIR, 0755); | ||
69 | if (rv == -1) | ||
70 | errExit("mkdir"); | ||
71 | if (chown(RUN_FIREJAIL_BASEDIR, 0, 0) < 0) | ||
72 | errExit("chown"); | ||
73 | if (chmod(RUN_FIREJAIL_BASEDIR, 0755) < 0) | ||
74 | errExit("chmod"); | ||
75 | } | ||
76 | else { // check /tmp/firejail directory belongs to root end exit if doesn't! | ||
77 | if (s.st_uid != 0 || s.st_gid != 0) { | ||
78 | fprintf(stderr, "Error: non-root %s directory, exiting...\n", RUN_FIREJAIL_DIR); | ||
79 | exit(1); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | if (stat(RUN_FIREJAIL_DIR, &s)) { | ||
84 | if (arg_debug) | ||
85 | printf("Creating %s directory\n", RUN_FIREJAIL_DIR); | ||
86 | /* coverity[toctou] */ | ||
87 | int rv = mkdir(RUN_FIREJAIL_DIR, 0755); | ||
88 | if (rv == -1) | ||
89 | errExit("mkdir"); | ||
90 | if (chown(RUN_FIREJAIL_DIR, 0, 0) < 0) | ||
91 | errExit("chown"); | ||
92 | if (chmod(RUN_FIREJAIL_DIR, 0755) < 0) | ||
93 | errExit("chmod"); | ||
94 | } | ||
95 | |||
96 | if (stat(RUN_FIREJAIL_NETWORK_DIR, &s)) { | ||
97 | if (arg_debug) | ||
98 | printf("Creating %s directory\n", RUN_FIREJAIL_NETWORK_DIR); | ||
99 | |||
100 | if (mkdir(RUN_FIREJAIL_NETWORK_DIR, 0755) == -1) | ||
101 | errExit("mkdir"); | ||
102 | if (chown(RUN_FIREJAIL_NETWORK_DIR, 0, 0) < 0) | ||
103 | errExit("chown"); | ||
104 | if (chmod(RUN_FIREJAIL_NETWORK_DIR, 0755) < 0) | ||
105 | errExit("chmod"); | ||
106 | } | ||
107 | |||
108 | if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s)) { | ||
109 | if (arg_debug) | ||
110 | printf("Creating %s directory\n", RUN_FIREJAIL_BANDWIDTH_DIR); | ||
111 | if (mkdir(RUN_FIREJAIL_BANDWIDTH_DIR, 0755) == -1) | ||
112 | errExit("mkdir"); | ||
113 | if (chown(RUN_FIREJAIL_BANDWIDTH_DIR, 0, 0) < 0) | ||
114 | errExit("chown"); | ||
115 | if (chmod(RUN_FIREJAIL_BANDWIDTH_DIR, 0755) < 0) | ||
116 | errExit("chmod"); | ||
117 | } | ||
118 | |||
119 | if (stat(RUN_FIREJAIL_NAME_DIR, &s)) { | ||
120 | if (arg_debug) | ||
121 | printf("Creating %s directory\n", RUN_FIREJAIL_NAME_DIR); | ||
122 | if (mkdir(RUN_FIREJAIL_NAME_DIR, 0755) == -1) | ||
123 | errExit("mkdir"); | ||
124 | if (chown(RUN_FIREJAIL_NAME_DIR, 0, 0) < 0) | ||
125 | errExit("chown"); | ||
126 | if (chmod(RUN_FIREJAIL_NAME_DIR, 0755) < 0) | ||
127 | errExit("chmod"); | ||
128 | } | ||
129 | |||
130 | if (stat(RUN_FIREJAIL_X11_DIR, &s)) { | ||
131 | if (arg_debug) | ||
132 | printf("Creating %s directory\n", RUN_FIREJAIL_X11_DIR); | ||
133 | if (mkdir(RUN_FIREJAIL_X11_DIR, 0755) == -1) | ||
134 | errExit("mkdir"); | ||
135 | if (chown(RUN_FIREJAIL_X11_DIR, 0, 0) < 0) | ||
136 | errExit("chown"); | ||
137 | if (chmod(RUN_FIREJAIL_X11_DIR, 0755) < 0) | ||
138 | errExit("chmod"); | ||
139 | } | ||
140 | |||
141 | create_empty_dir(); | ||
142 | create_empty_file(); | ||
143 | } | ||
144 | 31 | ||
145 | 32 | ||
146 | // build /tmp/firejail/mnt directory | ||
147 | static int tmpfs_mounted = 0; | ||
148 | #ifdef HAVE_CHROOT | ||
149 | static void fs_build_remount_mnt_dir(void) { | ||
150 | tmpfs_mounted = 0; | ||
151 | fs_build_mnt_dir(); | ||
152 | } | ||
153 | #endif | ||
154 | |||
155 | void fs_build_mnt_dir(void) { | ||
156 | struct stat s; | ||
157 | fs_build_firejail_dir(); | ||
158 | |||
159 | // create /run/firejail/mnt directory | ||
160 | if (stat(RUN_MNT_DIR, &s)) { | ||
161 | if (arg_debug) | ||
162 | printf("Creating %s directory\n", RUN_MNT_DIR); | ||
163 | /* coverity[toctou] */ | ||
164 | int rv = mkdir(RUN_MNT_DIR, 0755); | ||
165 | if (rv == -1) | ||
166 | errExit("mkdir"); | ||
167 | if (chown(RUN_MNT_DIR, 0, 0) < 0) | ||
168 | errExit("chown"); | ||
169 | if (chmod(RUN_MNT_DIR, 0755) < 0) | ||
170 | errExit("chmod"); | ||
171 | } | ||
172 | |||
173 | // ... and mount tmpfs on top of it | ||
174 | if (!tmpfs_mounted) { | ||
175 | // mount tmpfs on top of /run/firejail/mnt | ||
176 | if (arg_debug) | ||
177 | printf("Mounting tmpfs on %s directory\n", RUN_MNT_DIR); | ||
178 | if (mount("tmpfs", RUN_MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
179 | errExit("mounting /tmp/firejail/mnt"); | ||
180 | tmpfs_mounted = 1; | ||
181 | fs_logger2("tmpfs", RUN_MNT_DIR); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | // grab a copy of cp command | ||
186 | void fs_build_cp_command(void) { | ||
187 | struct stat s; | ||
188 | fs_build_mnt_dir(); | ||
189 | if (stat(RUN_CP_COMMAND, &s)) { | ||
190 | char* fname = realpath("/bin/cp", NULL); | ||
191 | if (fname == NULL) { | ||
192 | fprintf(stderr, "Error: /bin/cp not found\n"); | ||
193 | exit(1); | ||
194 | } | ||
195 | if (stat(fname, &s)) { | ||
196 | fprintf(stderr, "Error: /bin/cp not found\n"); | ||
197 | exit(1); | ||
198 | } | ||
199 | if (is_link(fname)) { | ||
200 | fprintf(stderr, "Error: invalid /bin/cp file\n"); | ||
201 | exit(1); | ||
202 | } | ||
203 | int rv = copy_file(fname, RUN_CP_COMMAND); | ||
204 | if (rv) { | ||
205 | fprintf(stderr, "Error: cannot access /bin/cp\n"); | ||
206 | exit(1); | ||
207 | } | ||
208 | /* coverity[toctou] */ | ||
209 | if (chown(RUN_CP_COMMAND, 0, 0)) | ||
210 | errExit("chown"); | ||
211 | if (chmod(RUN_CP_COMMAND, 0755)) | ||
212 | errExit("chmod"); | ||
213 | |||
214 | free(fname); | ||
215 | } | ||
216 | } | ||
217 | |||
218 | // delete the temporary cp command | ||
219 | void fs_delete_cp_command(void) { | ||
220 | unlink(RUN_CP_COMMAND); | ||
221 | } | ||
222 | 33 | ||
223 | //*********************************************** | 34 | //*********************************************** |
224 | // process profile file | 35 | // process profile file |
@@ -228,6 +39,8 @@ typedef enum { | |||
228 | BLACKLIST_NOLOG, | 39 | BLACKLIST_NOLOG, |
229 | MOUNT_READONLY, | 40 | MOUNT_READONLY, |
230 | MOUNT_TMPFS, | 41 | MOUNT_TMPFS, |
42 | MOUNT_NOEXEC, | ||
43 | MOUNT_RDWR, | ||
231 | OPERATION_MAX | 44 | OPERATION_MAX |
232 | } OPERATION; | 45 | } OPERATION; |
233 | 46 | ||
@@ -242,14 +55,9 @@ static void disable_file(OPERATION op, const char *filename) { | |||
242 | assert(op <OPERATION_MAX); | 55 | assert(op <OPERATION_MAX); |
243 | last_disable = UNSUCCESSFUL; | 56 | last_disable = UNSUCCESSFUL; |
244 | 57 | ||
245 | // rebuild /run/firejail directory in case tmpfs was mounted on top of /run | ||
246 | fs_build_firejail_dir(); | ||
247 | |||
248 | // Resolve all symlinks | 58 | // Resolve all symlinks |
249 | char* fname = realpath(filename, NULL); | 59 | char* fname = realpath(filename, NULL); |
250 | if (fname == NULL && errno != EACCES) { | 60 | if (fname == NULL && errno != EACCES) { |
251 | if (arg_debug) | ||
252 | printf("Warning (realpath): %s is an invalid file, skipping...\n", filename); | ||
253 | return; | 61 | return; |
254 | } | 62 | } |
255 | if (fname == NULL && errno == EACCES) { | 63 | if (fname == NULL && errno == EACCES) { |
@@ -298,9 +106,10 @@ static void disable_file(OPERATION op, const char *filename) { | |||
298 | // some distros put all executables under /usr/bin and make /bin a symbolic link | 106 | // some distros put all executables under /usr/bin and make /bin a symbolic link |
299 | if ((strcmp(fname, "/bin") == 0 || strcmp(fname, "/usr/bin") == 0) && | 107 | if ((strcmp(fname, "/bin") == 0 || strcmp(fname, "/usr/bin") == 0) && |
300 | is_link(filename) && | 108 | is_link(filename) && |
301 | S_ISDIR(s.st_mode)) | 109 | S_ISDIR(s.st_mode)) { |
302 | fprintf(stderr, "Warning: %s directory link was not blacklisted\n", filename); | 110 | if (!arg_quiet) |
303 | 111 | fprintf(stderr, "Warning: %s directory link was not blacklisted\n", filename); | |
112 | } | ||
304 | else { | 113 | else { |
305 | if (arg_debug) | 114 | if (arg_debug) |
306 | printf("Disable %s\n", fname); | 115 | printf("Disable %s\n", fname); |
@@ -332,6 +141,18 @@ static void disable_file(OPERATION op, const char *filename) { | |||
332 | fs_rdonly(fname); | 141 | fs_rdonly(fname); |
333 | // todo: last_disable = SUCCESSFUL; | 142 | // todo: last_disable = SUCCESSFUL; |
334 | } | 143 | } |
144 | else if (op == MOUNT_RDWR) { | ||
145 | if (arg_debug) | ||
146 | printf("Mounting read-only %s\n", fname); | ||
147 | fs_rdwr(fname); | ||
148 | // todo: last_disable = SUCCESSFUL; | ||
149 | } | ||
150 | else if (op == MOUNT_NOEXEC) { | ||
151 | if (arg_debug) | ||
152 | printf("Mounting noexec %s\n", fname); | ||
153 | fs_noexec(fname); | ||
154 | // todo: last_disable = SUCCESSFUL; | ||
155 | } | ||
335 | else if (op == MOUNT_TMPFS) { | 156 | else if (op == MOUNT_TMPFS) { |
336 | if (S_ISDIR(s.st_mode)) { | 157 | if (S_ISDIR(s.st_mode)) { |
337 | if (arg_debug) | 158 | if (arg_debug) |
@@ -361,7 +182,7 @@ static void globbing(OPERATION op, const char *pattern, const char *noblacklist[ | |||
361 | glob_t globbuf; | 182 | glob_t globbuf; |
362 | // Profiles contain blacklists for files that might not exist on a user's machine. | 183 | // Profiles contain blacklists for files that might not exist on a user's machine. |
363 | // GLOB_NOCHECK makes that okay. | 184 | // GLOB_NOCHECK makes that okay. |
364 | int globerr = glob(pattern, GLOB_NOCHECK | GLOB_NOSORT, NULL, &globbuf); | 185 | int globerr = glob(pattern, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf); |
365 | if (globerr) { | 186 | if (globerr) { |
366 | fprintf(stderr, "Error: failed to glob pattern %s\n", pattern); | 187 | fprintf(stderr, "Error: failed to glob pattern %s\n", pattern); |
367 | exit(1); | 188 | exit(1); |
@@ -426,21 +247,13 @@ void fs_blacklist(void) { | |||
426 | 247 | ||
427 | // process bind command | 248 | // process bind command |
428 | if (strncmp(entry->data, "bind ", 5) == 0) { | 249 | if (strncmp(entry->data, "bind ", 5) == 0) { |
250 | struct stat s; | ||
429 | char *dname1 = entry->data + 5; | 251 | char *dname1 = entry->data + 5; |
430 | char *dname2 = split_comma(dname1); | 252 | char *dname2 = split_comma(dname1); |
431 | if (dname2 == NULL) { | 253 | if (dname2 == NULL || |
432 | fprintf(stderr, "Error: second directory missing in bind command\n"); | 254 | stat(dname1, &s) == -1 || |
433 | entry = entry->next; | 255 | stat(dname2, &s) == -1) { |
434 | continue; | 256 | fprintf(stderr, "Error: invalid bind command, directory missing\n"); |
435 | } | ||
436 | struct stat s; | ||
437 | if (stat(dname1, &s) == -1) { | ||
438 | fprintf(stderr, "Error: cannot find %s for bind command\n", dname1); | ||
439 | entry = entry->next; | ||
440 | continue; | ||
441 | } | ||
442 | if (stat(dname2, &s) == -1) { | ||
443 | fprintf(stderr, "Error: cannot find %s for bind command\n", dname2); | ||
444 | entry = entry->next; | 257 | entry = entry->next; |
445 | continue; | 258 | continue; |
446 | } | 259 | } |
@@ -452,11 +265,8 @@ void fs_blacklist(void) { | |||
452 | if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) | 265 | if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) |
453 | errExit("mount bind"); | 266 | errExit("mount bind"); |
454 | /* coverity[toctou] */ | 267 | /* coverity[toctou] */ |
455 | if (chown(dname2, s.st_uid, s.st_gid) == -1) | 268 | if (set_perms(dname2, s.st_uid, s.st_gid,s.st_mode)) |
456 | errExit("mount-bind chown"); | 269 | errExit("set_perms"); |
457 | /* coverity[toctou] */ | ||
458 | if (chmod(dname2, s.st_mode) == -1) | ||
459 | errExit("mount-bind chmod"); | ||
460 | 270 | ||
461 | entry = entry->next; | 271 | entry = entry->next; |
462 | continue; | 272 | continue; |
@@ -464,12 +274,40 @@ void fs_blacklist(void) { | |||
464 | 274 | ||
465 | // Process noblacklist command | 275 | // Process noblacklist command |
466 | if (strncmp(entry->data, "noblacklist ", 12) == 0) { | 276 | if (strncmp(entry->data, "noblacklist ", 12) == 0) { |
467 | if (noblacklist_c >= noblacklist_m) { | 277 | char **paths = build_paths(); |
468 | noblacklist_m *= 2; | 278 | |
469 | noblacklist = realloc(noblacklist, sizeof(*noblacklist) * noblacklist_m); | 279 | char *enames[sizeof(paths)+1] = {0}; |
470 | if (noblacklist == NULL) | 280 | int i = 0; |
471 | errExit("failed increasing memory for noblacklist entries");} | 281 | |
472 | noblacklist[noblacklist_c++] = expand_home(entry->data + 12, homedir); | 282 | if (strncmp(entry->data + 12, "${PATH}", 7) == 0) { |
283 | // expand ${PATH} macro | ||
284 | while (paths[i] != NULL) { | ||
285 | if (asprintf(&enames[i], "%s%s", paths[i], entry->data + 19) == -1) | ||
286 | errExit("asprintf"); | ||
287 | i++; | ||
288 | } | ||
289 | } else { | ||
290 | // expand ${HOME} macro if found or pass as is | ||
291 | enames[0] = expand_home(entry->data + 12, homedir); | ||
292 | enames[1] = NULL; | ||
293 | } | ||
294 | |||
295 | i = 0; | ||
296 | while (enames[i] != NULL) { | ||
297 | if (noblacklist_c >= noblacklist_m) { | ||
298 | noblacklist_m *= 2; | ||
299 | noblacklist = realloc(noblacklist, sizeof(*noblacklist) * noblacklist_m); | ||
300 | if (noblacklist == NULL) | ||
301 | errExit("failed increasing memory for noblacklist entries"); | ||
302 | } | ||
303 | noblacklist[noblacklist_c++] = enames[i]; | ||
304 | i++; | ||
305 | } | ||
306 | |||
307 | while (enames[i] != NULL) { | ||
308 | free(enames[i]); | ||
309 | } | ||
310 | |||
473 | entry = entry->next; | 311 | entry = entry->next; |
474 | continue; | 312 | continue; |
475 | } | 313 | } |
@@ -487,10 +325,32 @@ void fs_blacklist(void) { | |||
487 | ptr = entry->data + 10; | 325 | ptr = entry->data + 10; |
488 | op = MOUNT_READONLY; | 326 | op = MOUNT_READONLY; |
489 | } | 327 | } |
328 | else if (strncmp(entry->data, "read-write ", 11) == 0) { | ||
329 | ptr = entry->data + 11; | ||
330 | op = MOUNT_RDWR; | ||
331 | } | ||
332 | else if (strncmp(entry->data, "noexec ", 7) == 0) { | ||
333 | ptr = entry->data + 7; | ||
334 | op = MOUNT_NOEXEC; | ||
335 | } | ||
490 | else if (strncmp(entry->data, "tmpfs ", 6) == 0) { | 336 | else if (strncmp(entry->data, "tmpfs ", 6) == 0) { |
491 | ptr = entry->data + 6; | 337 | ptr = entry->data + 6; |
492 | op = MOUNT_TMPFS; | 338 | op = MOUNT_TMPFS; |
493 | } | 339 | } |
340 | else if (strncmp(entry->data, "mkdir ", 6) == 0) { | ||
341 | EUID_USER(); | ||
342 | fs_mkdir(entry->data + 6); | ||
343 | EUID_ROOT(); | ||
344 | entry = entry->next; | ||
345 | continue; | ||
346 | } | ||
347 | else if (strncmp(entry->data, "mkfile ", 7) == 0) { | ||
348 | EUID_USER(); | ||
349 | fs_mkfile(entry->data + 7); | ||
350 | EUID_ROOT(); | ||
351 | entry = entry->next; | ||
352 | continue; | ||
353 | } | ||
494 | else { | 354 | else { |
495 | fprintf(stderr, "Error: invalid profile line %s\n", entry->data); | 355 | fprintf(stderr, "Error: invalid profile line %s\n", entry->data); |
496 | entry = entry->next; | 356 | entry = entry->next; |
@@ -542,38 +402,56 @@ void fs_rdonly(const char *dir) { | |||
542 | int rv = stat(dir, &s); | 402 | int rv = stat(dir, &s); |
543 | if (rv == 0) { | 403 | if (rv == 0) { |
544 | // mount --bind /bin /bin | 404 | // mount --bind /bin /bin |
545 | if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
546 | errExit("mount read-only"); | ||
547 | // mount --bind -o remount,ro /bin | 405 | // mount --bind -o remount,ro /bin |
548 | if (mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL) < 0) | 406 | if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || |
407 | mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL) < 0) | ||
549 | errExit("mount read-only"); | 408 | errExit("mount read-only"); |
550 | fs_logger2("read-only", dir); | 409 | fs_logger2("read-only", dir); |
551 | } | 410 | } |
552 | } | 411 | } |
553 | void fs_rdonly_noexit(const char *dir) { | 412 | |
413 | static void fs_rdwr(const char *dir) { | ||
414 | assert(dir); | ||
415 | // check directory exists | ||
416 | struct stat s; | ||
417 | int rv = stat(dir, &s); | ||
418 | if (rv == 0) { | ||
419 | // if the file is outside /home directory, allow only root user | ||
420 | uid_t u = getuid(); | ||
421 | if (u != 0 && s.st_uid != u) { | ||
422 | if (!arg_quiet) | ||
423 | fprintf(stderr, "Warning: you are not allowed to change %s to read-write\n", dir); | ||
424 | return; | ||
425 | } | ||
426 | |||
427 | // mount --bind /bin /bin | ||
428 | // mount --bind -o remount,rw /bin | ||
429 | if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
430 | mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_REC, NULL) < 0) | ||
431 | errExit("mount read-write"); | ||
432 | fs_logger2("read-write", dir); | ||
433 | } | ||
434 | } | ||
435 | |||
436 | void fs_noexec(const char *dir) { | ||
554 | assert(dir); | 437 | assert(dir); |
555 | // check directory exists | 438 | // check directory exists |
556 | struct stat s; | 439 | struct stat s; |
557 | int rv = stat(dir, &s); | 440 | int rv = stat(dir, &s); |
558 | if (rv == 0) { | 441 | if (rv == 0) { |
559 | int merr = 0; | ||
560 | // mount --bind /bin /bin | 442 | // mount --bind /bin /bin |
561 | if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
562 | merr = 1; | ||
563 | // mount --bind -o remount,ro /bin | 443 | // mount --bind -o remount,ro /bin |
564 | if (mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL) < 0) | 444 | if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || |
565 | merr = 1; | 445 | mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_NOEXEC|MS_NODEV|MS_NOSUID|MS_REC, NULL) < 0) |
566 | if (merr) | 446 | errExit("mount noexec"); |
567 | fprintf(stderr, "Warning: cannot mount %s read-only\n", dir); | 447 | fs_logger2("noexec", dir); |
568 | else | ||
569 | fs_logger2("read-only", dir); | ||
570 | } | 448 | } |
571 | } | 449 | } |
572 | 450 | ||
451 | |||
452 | |||
573 | // mount /proc and /sys directories | 453 | // mount /proc and /sys directories |
574 | void fs_proc_sys_dev_boot(void) { | 454 | void fs_proc_sys_dev_boot(void) { |
575 | struct stat s; | ||
576 | |||
577 | if (arg_debug) | 455 | if (arg_debug) |
578 | printf("Remounting /proc and /proc/sys filesystems\n"); | 456 | printf("Remounting /proc and /proc/sys filesystems\n"); |
579 | if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) | 457 | if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) |
@@ -581,10 +459,8 @@ void fs_proc_sys_dev_boot(void) { | |||
581 | fs_logger("remount /proc"); | 459 | fs_logger("remount /proc"); |
582 | 460 | ||
583 | // remount /proc/sys readonly | 461 | // remount /proc/sys readonly |
584 | if (mount("/proc/sys", "/proc/sys", NULL, MS_BIND | MS_REC, NULL) < 0) | 462 | if (mount("/proc/sys", "/proc/sys", NULL, MS_BIND | MS_REC, NULL) < 0 || |
585 | errExit("mounting /proc/sys"); | 463 | mount(NULL, "/proc/sys", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, NULL) < 0) |
586 | |||
587 | if (mount(NULL, "/proc/sys", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, NULL) < 0) | ||
588 | errExit("mounting /proc/sys"); | 464 | errExit("mounting /proc/sys"); |
589 | fs_logger("read-only /proc/sys"); | 465 | fs_logger("read-only /proc/sys"); |
590 | 466 | ||
@@ -601,114 +477,71 @@ void fs_proc_sys_dev_boot(void) { | |||
601 | fs_logger("remount /sys"); | 477 | fs_logger("remount /sys"); |
602 | } | 478 | } |
603 | 479 | ||
604 | if (stat("/sys/firmware", &s) == 0) { | 480 | disable_file(BLACKLIST_FILE, "/sys/firmware"); |
605 | disable_file(BLACKLIST_FILE, "/sys/firmware"); | 481 | disable_file(BLACKLIST_FILE, "/sys/hypervisor"); |
606 | } | 482 | { // allow user access to /sys/fs if "--noblacklist=/sys/fs" is present on the command line |
607 | 483 | EUID_USER(); | |
608 | if (stat("/sys/hypervisor", &s) == 0) { | 484 | profile_add("blacklist /sys/fs"); |
609 | disable_file(BLACKLIST_FILE, "/sys/hypervisor"); | 485 | EUID_ROOT(); |
610 | } | ||
611 | |||
612 | if (stat("/sys/fs", &s) == 0) { | ||
613 | disable_file(BLACKLIST_FILE, "/sys/fs"); | ||
614 | } | ||
615 | |||
616 | if (stat("/sys/module", &s) == 0) { | ||
617 | disable_file(BLACKLIST_FILE, "/sys/module"); | ||
618 | } | 486 | } |
619 | 487 | disable_file(BLACKLIST_FILE, "/sys/module"); | |
620 | if (stat("/sys/power", &s) == 0) { | 488 | disable_file(BLACKLIST_FILE, "/sys/power"); |
621 | disable_file(BLACKLIST_FILE, "/sys/power"); | 489 | disable_file(BLACKLIST_FILE, "/sys/kernel/debug"); |
622 | } | 490 | disable_file(BLACKLIST_FILE, "/sys/kernel/vmcoreinfo"); |
623 | 491 | disable_file(BLACKLIST_FILE, "/sys/kernel/uevent_helper"); | |
624 | // if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0) | 492 | |
625 | // errExit("mounting /sys"); | 493 | // various /proc/sys files |
626 | 494 | disable_file(BLACKLIST_FILE, "/proc/sys/security"); | |
627 | // Disable SysRq | 495 | disable_file(BLACKLIST_FILE, "/proc/sys/efi/vars"); |
628 | // a linux box can be shut down easily using the following commands (as root): | 496 | disable_file(BLACKLIST_FILE, "/proc/sys/fs/binfmt_misc"); |
629 | // # echo 1 > /proc/sys/kernel/sysrq | 497 | disable_file(BLACKLIST_FILE, "/proc/sys/kernel/core_pattern"); |
630 | // #echo b > /proc/sysrq-trigger | 498 | disable_file(BLACKLIST_FILE, "/proc/sys/kernel/modprobe"); |
631 | // for more information see https://www.kernel.org/doc/Documentation/sysrq.txt | 499 | disable_file(BLACKLIST_FILE, "/proc/sysrq-trigger"); |
632 | if (arg_debug) | 500 | disable_file(BLACKLIST_FILE, "/proc/sys/kernel/hotplug"); |
633 | printf("Disable /proc/sysrq-trigger\n"); | 501 | disable_file(BLACKLIST_FILE, "/proc/sys/vm/panic_on_oom"); |
634 | fs_rdonly_noexit("/proc/sysrq-trigger"); | 502 | |
635 | 503 | // various /proc files | |
636 | // disable hotplug and uevent_helper | 504 | disable_file(BLACKLIST_FILE, "/proc/irq"); |
637 | if (arg_debug) | 505 | disable_file(BLACKLIST_FILE, "/proc/bus"); |
638 | printf("Disable /proc/sys/kernel/hotplug\n"); | 506 | disable_file(BLACKLIST_FILE, "/proc/config.gz"); |
639 | fs_rdonly_noexit("/proc/sys/kernel/hotplug"); | 507 | disable_file(BLACKLIST_FILE, "/proc/sched_debug"); |
640 | if (arg_debug) | 508 | disable_file(BLACKLIST_FILE, "/proc/timer_list"); |
641 | printf("Disable /sys/kernel/uevent_helper\n"); | 509 | disable_file(BLACKLIST_FILE, "/proc/timer_stats"); |
642 | fs_rdonly_noexit("/sys/kernel/uevent_helper"); | ||
643 | |||
644 | // read-only /proc/irq and /proc/bus | ||
645 | if (arg_debug) | ||
646 | printf("Disable /proc/irq\n"); | ||
647 | fs_rdonly_noexit("/proc/irq"); | ||
648 | if (arg_debug) | ||
649 | printf("Disable /proc/bus\n"); | ||
650 | fs_rdonly_noexit("/proc/bus"); | ||
651 | |||
652 | // disable /proc/kcore | ||
653 | disable_file(BLACKLIST_FILE, "/proc/kcore"); | 510 | disable_file(BLACKLIST_FILE, "/proc/kcore"); |
654 | |||
655 | // disable /proc/kallsyms | ||
656 | disable_file(BLACKLIST_FILE, "/proc/kallsyms"); | 511 | disable_file(BLACKLIST_FILE, "/proc/kallsyms"); |
512 | disable_file(BLACKLIST_FILE, "/proc/mem"); | ||
513 | disable_file(BLACKLIST_FILE, "/proc/kmem"); | ||
657 | 514 | ||
658 | // disable /boot | 515 | // remove kernel symbol information |
659 | if (stat("/boot", &s) == 0) { | 516 | if (!arg_allow_debuggers) { |
660 | if (arg_debug) | 517 | disable_file(BLACKLIST_FILE, "/usr/src/linux"); |
661 | printf("Disable /boot directory\n"); | 518 | disable_file(BLACKLIST_FILE, "/lib/modules"); |
519 | disable_file(BLACKLIST_FILE, "/usr/lib/debug"); | ||
662 | disable_file(BLACKLIST_FILE, "/boot"); | 520 | disable_file(BLACKLIST_FILE, "/boot"); |
663 | } | 521 | } |
664 | 522 | ||
665 | // disable /selinux | 523 | // disable /selinux |
666 | if (stat("/selinux", &s) == 0) { | 524 | disable_file(BLACKLIST_FILE, "/selinux"); |
667 | if (arg_debug) | ||
668 | printf("Disable /selinux directory\n"); | ||
669 | disable_file(BLACKLIST_FILE, "/selinux"); | ||
670 | } | ||
671 | 525 | ||
672 | // disable /dev/port | 526 | // disable /dev/port |
673 | if (stat("/dev/port", &s) == 0) { | 527 | disable_file(BLACKLIST_FILE, "/dev/port"); |
674 | disable_file(BLACKLIST_FILE, "/dev/port"); | ||
675 | } | ||
676 | 528 | ||
677 | if (getuid() != 0) { | 529 | if (getuid() != 0) { |
678 | // disable /dev/kmsg | 530 | // disable /dev/kmsg and /proc/kmsg |
679 | if (stat("/dev/kmsg", &s) == 0) { | 531 | disable_file(BLACKLIST_FILE, "/dev/kmsg"); |
680 | disable_file(BLACKLIST_FILE, "/dev/kmsg"); | 532 | disable_file(BLACKLIST_FILE, "/proc/kmsg"); |
681 | } | ||
682 | |||
683 | // disable /proc/kmsg | ||
684 | if (stat("/proc/kmsg", &s) == 0) { | ||
685 | disable_file(BLACKLIST_FILE, "/proc/kmsg"); | ||
686 | } | ||
687 | } | 533 | } |
688 | } | 534 | } |
689 | 535 | ||
690 | // disable firejail configuration in /etc/firejail and in ~/.config/firejail | 536 | // disable firejail configuration in /etc/firejail and in ~/.config/firejail |
691 | static void disable_firejail_config(void) { | 537 | static void disable_config(void) { |
692 | struct stat s; | 538 | struct stat s; |
693 | if (stat("/etc/firejail", &s) == 0) | ||
694 | disable_file(BLACKLIST_FILE, "/etc/firejail"); | ||
695 | 539 | ||
696 | char *fname; | 540 | char *fname; |
697 | if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) | 541 | if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) |
698 | errExit("asprintf"); | 542 | errExit("asprintf"); |
699 | if (stat(fname, &s) == 0) | 543 | if (stat(fname, &s) == 0) |
700 | disable_file(BLACKLIST_FILE, fname); | 544 | disable_file(BLACKLIST_FILE, fname); |
701 | |||
702 | if (stat("/usr/local/etc/firejail", &s) == 0) | ||
703 | disable_file(BLACKLIST_FILE, "/usr/local/etc/firejail"); | ||
704 | |||
705 | if (strcmp(PREFIX, "/usr/local")) { | ||
706 | if (asprintf(&fname, "%s/etc/firejail", PREFIX) == -1) | ||
707 | errExit("asprintf"); | ||
708 | if (stat(fname, &s) == 0) | ||
709 | disable_file(BLACKLIST_FILE, fname); | ||
710 | } | ||
711 | |||
712 | free(fname); | 545 | free(fname); |
713 | 546 | ||
714 | // disable run time information | 547 | // disable run time information |
@@ -725,8 +558,23 @@ static void disable_firejail_config(void) { | |||
725 | 558 | ||
726 | // build a basic read-only filesystem | 559 | // build a basic read-only filesystem |
727 | void fs_basic_fs(void) { | 560 | void fs_basic_fs(void) { |
561 | uid_t uid = getuid(); | ||
562 | |||
728 | if (arg_debug) | 563 | if (arg_debug) |
729 | printf("Mounting read-only /bin, /sbin, /lib, /lib32, /lib64, /usr, /etc, /var\n"); | 564 | printf("Mounting read-only /bin, /sbin, /lib, /lib32, /lib64, /usr"); |
565 | if (!arg_writable_etc) { | ||
566 | fs_rdonly("/etc"); | ||
567 | if (uid) | ||
568 | fs_noexec("/etc"); | ||
569 | if (arg_debug) printf(", /etc"); | ||
570 | } | ||
571 | if (!arg_writable_var) { | ||
572 | fs_rdonly("/var"); | ||
573 | if (uid) | ||
574 | fs_noexec("/var"); | ||
575 | if (arg_debug) printf(", /var"); | ||
576 | } | ||
577 | if (arg_debug) printf("\n"); | ||
730 | fs_rdonly("/bin"); | 578 | fs_rdonly("/bin"); |
731 | fs_rdonly("/sbin"); | 579 | fs_rdonly("/sbin"); |
732 | fs_rdonly("/lib"); | 580 | fs_rdonly("/lib"); |
@@ -734,12 +582,10 @@ void fs_basic_fs(void) { | |||
734 | fs_rdonly("/lib32"); | 582 | fs_rdonly("/lib32"); |
735 | fs_rdonly("/libx32"); | 583 | fs_rdonly("/libx32"); |
736 | fs_rdonly("/usr"); | 584 | fs_rdonly("/usr"); |
737 | fs_rdonly("/etc"); | ||
738 | fs_rdonly("/var"); | ||
739 | 585 | ||
740 | // update /var directory in order to support multiple sandboxes running on the same root directory | 586 | // update /var directory in order to support multiple sandboxes running on the same root directory |
741 | if (!arg_private_dev) | 587 | // if (!arg_private_dev) |
742 | fs_dev_shm(); | 588 | // fs_dev_shm(); |
743 | fs_var_lock(); | 589 | fs_var_lock(); |
744 | fs_var_tmp(); | 590 | fs_var_tmp(); |
745 | fs_var_log(); | 591 | fs_var_log(); |
@@ -750,10 +596,51 @@ void fs_basic_fs(void) { | |||
750 | // don't leak user information | 596 | // don't leak user information |
751 | restrict_users(); | 597 | restrict_users(); |
752 | 598 | ||
753 | disable_firejail_config(); | 599 | // when starting as root, firejail config is not disabled; |
600 | // this mode could be used to install and test new software by chaining | ||
601 | // firejail sandboxes (firejail --force) | ||
602 | if (uid) | ||
603 | disable_config(); | ||
754 | } | 604 | } |
755 | 605 | ||
756 | 606 | ||
607 | |||
608 | #ifdef HAVE_OVERLAYFS | ||
609 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { | ||
610 | struct stat s; | ||
611 | char *dirname; | ||
612 | |||
613 | // create ~/.firejail directory | ||
614 | if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1) | ||
615 | errExit("asprintf"); | ||
616 | if (stat(dirname, &s) == -1) { | ||
617 | mkdir_attr(dirname, 0700, 0, 0); | ||
618 | } | ||
619 | else if (is_link(dirname)) { | ||
620 | fprintf(stderr, "Error: invalid ~/.firejail directory\n"); | ||
621 | exit(1); | ||
622 | } | ||
623 | free(dirname); | ||
624 | |||
625 | // check overlay directory | ||
626 | if (asprintf(&dirname, "%s/.firejail/%s", cfg.homedir, subdirname) == -1) | ||
627 | errExit("asprintf"); | ||
628 | if (is_link(dirname)) { | ||
629 | fprintf(stderr, "Error: overlay directory is a symbolic link\n"); | ||
630 | exit(1); | ||
631 | } | ||
632 | if (allow_reuse == 0) { | ||
633 | if (stat(dirname, &s) == 0) { | ||
634 | fprintf(stderr, "Error: overlay directory already exists: %s\n", dirname); | ||
635 | exit(1); | ||
636 | } | ||
637 | } | ||
638 | |||
639 | return dirname; | ||
640 | } | ||
641 | |||
642 | |||
643 | |||
757 | // mount overlayfs on top of / directory | 644 | // mount overlayfs on top of / directory |
758 | // mounting an overlay and chrooting into it: | 645 | // mounting an overlay and chrooting into it: |
759 | // | 646 | // |
@@ -806,48 +693,53 @@ void fs_overlayfs(void) { | |||
806 | if (major == 3 && minor < 18) | 693 | if (major == 3 && minor < 18) |
807 | oldkernel = 1; | 694 | oldkernel = 1; |
808 | 695 | ||
809 | // build overlay directories | ||
810 | fs_build_mnt_dir(); | ||
811 | |||
812 | char *oroot; | 696 | char *oroot; |
813 | if(asprintf(&oroot, "%s/oroot", RUN_MNT_DIR) == -1) | 697 | if(asprintf(&oroot, "%s/oroot", RUN_MNT_DIR) == -1) |
814 | errExit("asprintf"); | 698 | errExit("asprintf"); |
815 | if (mkdir(oroot, 0755)) | 699 | mkdir_attr(oroot, 0755, 0, 0); |
816 | errExit("mkdir"); | ||
817 | if (chown(oroot, 0, 0) < 0) | ||
818 | errExit("chown"); | ||
819 | if (chmod(oroot, 0755) < 0) | ||
820 | errExit("chmod"); | ||
821 | 700 | ||
701 | struct stat s; | ||
822 | char *basedir = RUN_MNT_DIR; | 702 | char *basedir = RUN_MNT_DIR; |
823 | if (arg_overlay_keep) { | 703 | if (arg_overlay_keep) { |
824 | // set base for working and diff directories | 704 | // set base for working and diff directories |
825 | basedir = cfg.overlay_dir; | 705 | basedir = cfg.overlay_dir; |
826 | if (mkdir(basedir, 0755) != 0) { | 706 | |
827 | fprintf(stderr, "Error: cannot create overlay directory\n"); | 707 | // does the overlay exist? |
828 | exit(1); | 708 | if (stat(basedir, &s) == 0) { |
709 | if (arg_overlay_reuse == 0) { | ||
710 | fprintf(stderr, "Error: overlay directory exists, but reuse is not allowed\n"); | ||
711 | exit(1); | ||
712 | } | ||
713 | } | ||
714 | else { | ||
715 | if (mkdir(basedir, 0755) != 0) { | ||
716 | fprintf(stderr, "Error: cannot create overlay directory\n"); | ||
717 | exit(1); | ||
718 | } | ||
829 | } | 719 | } |
830 | } | 720 | } |
831 | 721 | ||
832 | char *odiff; | 722 | char *odiff; |
833 | if(asprintf(&odiff, "%s/odiff", basedir) == -1) | 723 | if(asprintf(&odiff, "%s/odiff", basedir) == -1) |
834 | errExit("asprintf"); | 724 | errExit("asprintf"); |
835 | if (mkdir(odiff, 0755)) | 725 | |
836 | errExit("mkdir"); | 726 | // no need to check arg_overlay_reuse |
837 | if (chown(odiff, 0, 0) < 0) | 727 | if (stat(odiff, &s) != 0) { |
838 | errExit("chown"); | 728 | mkdir_attr(odiff, 0755, 0, 0); |
839 | if (chmod(odiff, 0755) < 0) | 729 | } |
840 | errExit("chmod"); | 730 | else if (set_perms(odiff, 0, 0, 0755)) |
731 | errExit("set_perms"); | ||
841 | 732 | ||
842 | char *owork; | 733 | char *owork; |
843 | if(asprintf(&owork, "%s/owork", basedir) == -1) | 734 | if(asprintf(&owork, "%s/owork", basedir) == -1) |
844 | errExit("asprintf"); | 735 | errExit("asprintf"); |
845 | if (mkdir(owork, 0755)) | 736 | |
846 | errExit("mkdir"); | 737 | // no need to check arg_overlay_reuse |
847 | if (chown(owork, 0, 0) < 0) | 738 | if (stat(owork, &s) != 0) { |
739 | mkdir_attr(owork, 0755, 0, 0); | ||
740 | } | ||
741 | else if (set_perms(owork, 0, 0, 0755)) | ||
848 | errExit("chown"); | 742 | errExit("chown"); |
849 | if (chmod(owork, 0755) < 0) | ||
850 | errExit("chmod"); | ||
851 | 743 | ||
852 | // mount overlayfs | 744 | // mount overlayfs |
853 | if (arg_debug) | 745 | if (arg_debug) |
@@ -899,21 +791,23 @@ void fs_overlayfs(void) { | |||
899 | 791 | ||
900 | if(asprintf(&hdiff, "%s/hdiff", basedir) == -1) | 792 | if(asprintf(&hdiff, "%s/hdiff", basedir) == -1) |
901 | errExit("asprintf"); | 793 | errExit("asprintf"); |
902 | if (mkdir(hdiff, S_IRWXU | S_IRWXG | S_IRWXO)) | 794 | |
903 | errExit("mkdir"); | 795 | // no need to check arg_overlay_reuse |
904 | if (chown(hdiff, 0, 0) < 0) | 796 | if (stat(hdiff, &s) != 0) { |
905 | errExit("chown"); | 797 | mkdir_attr(hdiff, S_IRWXU | S_IRWXG | S_IRWXO, 0, 0); |
906 | if (chmod(hdiff, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) | 798 | } |
907 | errExit("chmod"); | 799 | else if (set_perms(hdiff, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) |
800 | errExit("set_perms"); | ||
908 | 801 | ||
909 | if(asprintf(&hwork, "%s/hwork", basedir) == -1) | 802 | if(asprintf(&hwork, "%s/hwork", basedir) == -1) |
910 | errExit("asprintf"); | 803 | errExit("asprintf"); |
911 | if (mkdir(hwork, S_IRWXU | S_IRWXG | S_IRWXO)) | 804 | |
912 | errExit("mkdir"); | 805 | // no need to check arg_overlay_reuse |
913 | if (chown(hwork, 0, 0) < 0) | 806 | if (stat(hwork, &s) != 0) { |
914 | errExit("chown"); | 807 | mkdir_attr(hwork, S_IRWXU | S_IRWXG | S_IRWXO, 0, 0); |
915 | if (chmod(hwork, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) | 808 | } |
916 | errExit("chmod"); | 809 | else if (set_perms(hwork, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) |
810 | errExit("set_perms"); | ||
917 | 811 | ||
918 | // no homedir in overlay so now mount another overlay for /home | 812 | // no homedir in overlay so now mount another overlay for /home |
919 | if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1) | 813 | if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1) |
@@ -950,13 +844,30 @@ void fs_overlayfs(void) { | |||
950 | errExit("mounting /run"); | 844 | errExit("mounting /run"); |
951 | fs_logger("whitelist /run"); | 845 | fs_logger("whitelist /run"); |
952 | 846 | ||
847 | // mount-bind /tmp/.X11-unix directory | ||
848 | if (stat("/tmp/.X11-unix", &s) == 0) { | ||
849 | if (arg_debug) | ||
850 | printf("Mounting /tmp/.X11-unix\n"); | ||
851 | char *x11; | ||
852 | if (asprintf(&x11, "%s/tmp/.X11-unix", oroot) == -1) | ||
853 | errExit("asprintf"); | ||
854 | if (mount("/tmp/.X11-unix", x11, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
855 | fprintf(stderr, "Warning: cannot mount /tmp/.X11-unix in overlay\n"); | ||
856 | else | ||
857 | fs_logger("whitelist /tmp/.X11-unix"); | ||
858 | free(x11); | ||
859 | } | ||
860 | |||
953 | // chroot in the new filesystem | 861 | // chroot in the new filesystem |
862 | #ifdef HAVE_GCOV | ||
863 | __gcov_flush(); | ||
864 | #endif | ||
954 | if (chroot(oroot) == -1) | 865 | if (chroot(oroot) == -1) |
955 | errExit("chroot"); | 866 | errExit("chroot"); |
956 | 867 | ||
957 | // update /var directory in order to support multiple sandboxes running on the same root directory | 868 | // update /var directory in order to support multiple sandboxes running on the same root directory |
958 | if (!arg_private_dev) | 869 | // if (!arg_private_dev) |
959 | fs_dev_shm(); | 870 | // fs_dev_shm(); |
960 | fs_var_lock(); | 871 | fs_var_lock(); |
961 | fs_var_tmp(); | 872 | fs_var_tmp(); |
962 | fs_var_log(); | 873 | fs_var_log(); |
@@ -967,20 +878,18 @@ void fs_overlayfs(void) { | |||
967 | // don't leak user information | 878 | // don't leak user information |
968 | restrict_users(); | 879 | restrict_users(); |
969 | 880 | ||
970 | // when starting as root in overlay mode, firejail config is not disabled; | 881 | // when starting as root, firejail config is not disabled; |
971 | // this mode could be used to install and test new software by chaining | 882 | // this mode could be used to install and test new software by chaining |
972 | // firejail sandboxes (firejail --force) | 883 | // firejail sandboxes (firejail --force) |
973 | if (getuid() != 0) | 884 | if (getuid() != 0) |
974 | disable_firejail_config(); | 885 | disable_config(); |
975 | else | ||
976 | fprintf(stderr, "Warning: masking /etc/firejail disabled when starting the sandbox as root using --overlay option\n"); | ||
977 | 886 | ||
978 | // cleanup and exit | 887 | // cleanup and exit |
979 | free(option); | 888 | free(option); |
980 | free(oroot); | 889 | free(oroot); |
981 | free(odiff); | 890 | free(odiff); |
982 | } | 891 | } |
983 | 892 | #endif | |
984 | 893 | ||
985 | 894 | ||
986 | #ifdef HAVE_CHROOT | 895 | #ifdef HAVE_CHROOT |
@@ -991,6 +900,16 @@ int fs_check_chroot_dir(const char *rootdir) { | |||
991 | struct stat s; | 900 | struct stat s; |
992 | char *name; | 901 | char *name; |
993 | 902 | ||
903 | // rootdir has to be owned by root | ||
904 | if (stat(rootdir, &s) != 0) { | ||
905 | fprintf(stderr, "Error: cannot find chroot directory\n"); | ||
906 | return 1; | ||
907 | } | ||
908 | if (s.st_uid != 0) { | ||
909 | fprintf(stderr, "Error: chroot directory should be owned by root\n"); | ||
910 | return 1; | ||
911 | } | ||
912 | |||
994 | // check /dev | 913 | // check /dev |
995 | if (asprintf(&name, "%s/dev", rootdir) == -1) | 914 | if (asprintf(&name, "%s/dev", rootdir) == -1) |
996 | errExit("asprintf"); | 915 | errExit("asprintf"); |
@@ -1018,7 +937,7 @@ int fs_check_chroot_dir(const char *rootdir) { | |||
1018 | } | 937 | } |
1019 | free(name); | 938 | free(name); |
1020 | 939 | ||
1021 | // check /proc | 940 | // check /tmp |
1022 | if (asprintf(&name, "%s/tmp", rootdir) == -1) | 941 | if (asprintf(&name, "%s/tmp", rootdir) == -1) |
1023 | errExit("asprintf"); | 942 | errExit("asprintf"); |
1024 | if (stat(name, &s) == -1) { | 943 | if (stat(name, &s) == -1) { |
@@ -1026,16 +945,29 @@ int fs_check_chroot_dir(const char *rootdir) { | |||
1026 | return 1; | 945 | return 1; |
1027 | } | 946 | } |
1028 | free(name); | 947 | free(name); |
1029 | 948 | ||
1030 | // check /bin/bash | 949 | // check /bin/bash |
1031 | if (asprintf(&name, "%s/bin/bash", rootdir) == -1) | 950 | // if (asprintf(&name, "%s/bin/bash", rootdir) == -1) |
1032 | errExit("asprintf"); | 951 | // errExit("asprintf"); |
1033 | if (stat(name, &s) == -1) { | 952 | // if (stat(name, &s) == -1) { |
1034 | fprintf(stderr, "Error: cannot find /bin/bash in chroot directory\n"); | 953 | // fprintf(stderr, "Error: cannot find /bin/bash in chroot directory\n"); |
1035 | return 1; | 954 | // return 1; |
955 | // } | ||
956 | // free(name); | ||
957 | |||
958 | // check x11 socket directory | ||
959 | if (getenv("FIREJAIL_X11")) { | ||
960 | mask_x11_abstract_socket = 1; | ||
961 | char *name; | ||
962 | if (asprintf(&name, "%s/tmp/.X11-unix", rootdir) == -1) | ||
963 | errExit("asprintf"); | ||
964 | if (stat(name, &s) == -1) { | ||
965 | fprintf(stderr, "Error: cannot find /tmp/.X11-unix in chroot directory\n"); | ||
966 | return 1; | ||
967 | } | ||
968 | free(name); | ||
1036 | } | 969 | } |
1037 | free(name); | 970 | |
1038 | |||
1039 | return 0; | 971 | return 0; |
1040 | } | 972 | } |
1041 | 973 | ||
@@ -1043,77 +975,108 @@ int fs_check_chroot_dir(const char *rootdir) { | |||
1043 | void fs_chroot(const char *rootdir) { | 975 | void fs_chroot(const char *rootdir) { |
1044 | assert(rootdir); | 976 | assert(rootdir); |
1045 | 977 | ||
1046 | //*********************************** | 978 | if (checkcfg(CFG_CHROOT_DESKTOP)) { |
1047 | // mount-bind a /dev in rootdir | 979 | // mount-bind a /dev in rootdir |
1048 | //*********************************** | 980 | char *newdev; |
1049 | // mount /dev | 981 | if (asprintf(&newdev, "%s/dev", rootdir) == -1) |
1050 | char *newdev; | 982 | errExit("asprintf"); |
1051 | if (asprintf(&newdev, "%s/dev", rootdir) == -1) | 983 | if (arg_debug) |
1052 | errExit("asprintf"); | 984 | printf("Mounting /dev on %s\n", newdev); |
1053 | if (arg_debug) | 985 | if (mount("/dev", newdev, NULL, MS_BIND|MS_REC, NULL) < 0) |
1054 | printf("Mounting /dev on %s\n", newdev); | 986 | errExit("mounting /dev"); |
1055 | if (mount("/dev", newdev, NULL, MS_BIND|MS_REC, NULL) < 0) | 987 | free(newdev); |
1056 | errExit("mounting /dev"); | 988 | |
1057 | 989 | // x11 | |
1058 | // some older distros don't have a /run directory | 990 | if (getenv("FIREJAIL_X11")) { |
1059 | // create one by default | 991 | mask_x11_abstract_socket = 1; |
1060 | // no exit on error, let the user deal with any problems | 992 | char *newx11; |
1061 | char *rundir; | 993 | if (asprintf(&newx11, "%s/tmp/.X11-unix", rootdir) == -1) |
1062 | if (asprintf(&rundir, "%s/run", rootdir) == -1) | 994 | errExit("asprintf"); |
1063 | errExit("asprintf"); | 995 | if (arg_debug) |
1064 | if (!is_dir(rundir)) { | 996 | printf("Mounting /tmp/.X11-unix on %s\n", newx11); |
1065 | int rv = mkdir(rundir, 0755); | 997 | if (mount("/tmp/.X11-unix", newx11, NULL, MS_BIND|MS_REC, NULL) < 0) |
1066 | (void) rv; | 998 | errExit("mounting /tmp/.X11-unix"); |
1067 | rv = chown(rundir, 0, 0); | 999 | free(newx11); |
1068 | (void) rv; | 1000 | } |
1001 | |||
1002 | // create /run/firejail directory in chroot | ||
1003 | char *rundir; | ||
1004 | if (asprintf(&rundir, "%s/run", rootdir) == -1) | ||
1005 | errExit("asprintf"); | ||
1006 | create_empty_dir_as_root(rundir, 0755); | ||
1007 | free(rundir); | ||
1008 | if (asprintf(&rundir, "%s/run/firejail", rootdir) == -1) | ||
1009 | errExit("asprintf"); | ||
1010 | create_empty_dir_as_root(rundir, 0755); | ||
1011 | free(rundir); | ||
1012 | |||
1013 | // create /run/firejail/mnt directory in chroot and mount a tmpfs | ||
1014 | if (asprintf(&rundir, "%s/run/firejail/mnt", rootdir) == -1) | ||
1015 | errExit("asprintf"); | ||
1016 | create_empty_dir_as_root(rundir, 0755); | ||
1017 | if (mount("tmpfs", rundir, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
1018 | errExit("mounting /run/firejail/mnt"); | ||
1019 | fs_logger2("tmpfs", RUN_MNT_DIR); | ||
1020 | free(rundir); | ||
1021 | |||
1022 | // retrieve seccomp.protocol | ||
1023 | struct stat s; | ||
1024 | if (stat(RUN_SECCOMP_PROTOCOL, &s) == 0) { | ||
1025 | if (asprintf(&rundir, "%s%s", rootdir, RUN_SECCOMP_PROTOCOL) == -1) | ||
1026 | errExit("asprintf"); | ||
1027 | copy_file(RUN_SECCOMP_PROTOCOL, rundir, getuid(), getgid(), 0644); | ||
1028 | free(rundir); | ||
1029 | } | ||
1030 | |||
1031 | // copy /etc/resolv.conf in chroot directory | ||
1032 | // if resolv.conf in chroot is a symbolic link, this will fail | ||
1033 | // no exit on error, let the user deal with the problem | ||
1034 | char *fname; | ||
1035 | if (asprintf(&fname, "%s/etc/resolv.conf", rootdir) == -1) | ||
1036 | errExit("asprintf"); | ||
1037 | if (arg_debug) | ||
1038 | printf("Updating /etc/resolv.conf in %s\n", fname); | ||
1039 | if (is_link(fname)) { | ||
1040 | fprintf(stderr, "Error: invalid %s file\n", fname); | ||
1041 | exit(1); | ||
1042 | } | ||
1043 | if (copy_file("/etc/resolv.conf", fname, 0, 0, 0644) == -1) | ||
1044 | fprintf(stderr, "Warning: /etc/resolv.conf not initialized\n"); | ||
1069 | } | 1045 | } |
1070 | 1046 | ||
1071 | // copy /etc/resolv.conf in chroot directory | ||
1072 | // if resolv.conf in chroot is a symbolic link, this will fail | ||
1073 | // no exit on error, let the user deal with the problem | ||
1074 | char *fname; | ||
1075 | if (asprintf(&fname, "%s/etc/resolv.conf", rootdir) == -1) | ||
1076 | errExit("asprintf"); | ||
1077 | if (arg_debug) | ||
1078 | printf("Updating /etc/resolv.conf in %s\n", fname); | ||
1079 | if (is_link(fname)) { | ||
1080 | fprintf(stderr, "Error: invalid %s file\n", fname); | ||
1081 | exit(1); | ||
1082 | } | ||
1083 | if (copy_file("/etc/resolv.conf", fname) == -1) | ||
1084 | fprintf(stderr, "Warning: /etc/resolv.conf not initialized\n"); | ||
1085 | |||
1086 | // chroot into the new directory | 1047 | // chroot into the new directory |
1048 | #ifdef HAVE_GCOV | ||
1049 | __gcov_flush(); | ||
1050 | #endif | ||
1087 | if (arg_debug) | 1051 | if (arg_debug) |
1088 | printf("Chrooting into %s\n", rootdir); | 1052 | printf("Chrooting into %s\n", rootdir); |
1089 | if (chroot(rootdir) < 0) | 1053 | if (chroot(rootdir) < 0) |
1090 | errExit("chroot"); | 1054 | errExit("chroot"); |
1091 | // mount a new tmpfs in /run/firejail/mnt - the old one was lost in chroot | ||
1092 | fs_build_remount_mnt_dir(); | ||
1093 | |||
1094 | // update /var directory in order to support multiple sandboxes running on the same root directory | ||
1095 | if (!arg_private_dev) | ||
1096 | fs_dev_shm(); | ||
1097 | fs_var_lock(); | ||
1098 | fs_var_tmp(); | ||
1099 | fs_var_log(); | ||
1100 | fs_var_lib(); | ||
1101 | fs_var_cache(); | ||
1102 | fs_var_utmp(); | ||
1103 | 1055 | ||
1104 | // don't leak user information | 1056 | // create all other /run/firejail files and directories |
1105 | restrict_users(); | 1057 | preproc_build_firejail_dir(); |
1106 | 1058 | ||
1107 | disable_firejail_config(); | 1059 | if (checkcfg(CFG_CHROOT_DESKTOP)) { |
1060 | // update /var directory in order to support multiple sandboxes running on the same root directory | ||
1061 | // if (!arg_private_dev) | ||
1062 | // fs_dev_shm(); | ||
1063 | fs_var_lock(); | ||
1064 | fs_var_tmp(); | ||
1065 | fs_var_log(); | ||
1066 | fs_var_lib(); | ||
1067 | fs_var_cache(); | ||
1068 | fs_var_utmp(); | ||
1069 | |||
1070 | // don't leak user information | ||
1071 | restrict_users(); | ||
1072 | |||
1073 | // when starting as root, firejail config is not disabled; | ||
1074 | // this mode could be used to install and test new software by chaining | ||
1075 | // firejail sandboxes (firejail --force) | ||
1076 | if (getuid() != 0) | ||
1077 | disable_config(); | ||
1078 | } | ||
1108 | } | 1079 | } |
1109 | #endif | 1080 | #endif |
1110 | 1081 | ||
1111 | void fs_private_tmp(void) { | ||
1112 | // mount tmpfs on top of /run/firejail/mnt | ||
1113 | if (arg_debug) | ||
1114 | printf("Mounting tmpfs on /tmp directory\n"); | ||
1115 | if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) | ||
1116 | errExit("mounting /tmp/firejail/mnt"); | ||
1117 | fs_logger2("tmpfs", "/tmp"); | ||
1118 | } | ||
1119 | 1082 | ||
diff --git a/src/firejail/fs_bin.c b/src/firejail/fs_bin.c index c3d24aaac..6cc1bf3ab 100644 --- a/src/firejail/fs_bin.c +++ b/src/firejail/fs_bin.c | |||
@@ -28,6 +28,8 @@ static char *paths[] = { | |||
28 | "/usr/local/bin", | 28 | "/usr/local/bin", |
29 | "/usr/bin", | 29 | "/usr/bin", |
30 | "/bin", | 30 | "/bin", |
31 | "/usr/games", | ||
32 | "/usr/local/games", | ||
31 | "/usr/local/sbin", | 33 | "/usr/local/sbin", |
32 | "/usr/sbin", | 34 | "/usr/sbin", |
33 | "/sbin", | 35 | "/sbin", |
@@ -44,12 +46,38 @@ static char *check_dir_or_file(const char *name) { | |||
44 | 46 | ||
45 | int i = 0; | 47 | int i = 0; |
46 | while (paths[i]) { | 48 | while (paths[i]) { |
49 | // private-bin-no-local can be disabled in /etc/firejail/firejail.config | ||
50 | if (checkcfg(CFG_PRIVATE_BIN_NO_LOCAL) && strstr(paths[i], "local/")) { | ||
51 | i++; | ||
52 | continue; | ||
53 | } | ||
54 | |||
55 | // check file | ||
47 | if (asprintf(&fname, "%s/%s", paths[i], name) == -1) | 56 | if (asprintf(&fname, "%s/%s", paths[i], name) == -1) |
48 | errExit("asprintf"); | 57 | errExit("asprintf"); |
49 | if (arg_debug) | 58 | if (arg_debug) |
50 | printf("Checking %s/%s\n", paths[i], name); | 59 | printf("Checking %s/%s\n", paths[i], name); |
51 | if (stat(fname, &s) == 0 && !S_ISDIR(s.st_mode)) // do not allow directories | 60 | if (stat(fname, &s) == 0 && !S_ISDIR(s.st_mode)) { // do not allow directories |
61 | // check symlink to firejail executable in /usr/local/bin | ||
62 | if (strcmp(paths[i], "/usr/local/bin") == 0 && is_link(fname)) { | ||
63 | char *actual_path = realpath(fname, NULL); | ||
64 | if (actual_path) { | ||
65 | char *ptr = strstr(actual_path, "/firejail"); | ||
66 | if (ptr && strlen(ptr) == strlen("/firejail")) { | ||
67 | if (arg_debug) | ||
68 | printf("firejail exec symlink detected\n"); | ||
69 | free(actual_path); | ||
70 | free(fname); | ||
71 | fname = NULL; | ||
72 | i++; | ||
73 | continue; | ||
74 | } | ||
75 | free(actual_path); | ||
76 | } | ||
77 | |||
78 | } | ||
52 | break; // file found | 79 | break; // file found |
80 | } | ||
53 | 81 | ||
54 | free(fname); | 82 | free(fname); |
55 | fname = NULL; | 83 | fname = NULL; |
@@ -57,7 +85,8 @@ static char *check_dir_or_file(const char *name) { | |||
57 | } | 85 | } |
58 | 86 | ||
59 | if (!fname) { | 87 | if (!fname) { |
60 | // fprintf(stderr, "Warning: file %s not found\n", name); | 88 | if (arg_debug) |
89 | fprintf(stderr, "Warning: file %s not found\n", name); | ||
61 | return NULL; | 90 | return NULL; |
62 | } | 91 | } |
63 | 92 | ||
@@ -108,16 +137,16 @@ void fs_check_bin_list(void) { | |||
108 | } | 137 | } |
109 | 138 | ||
110 | if (*newlist == '\0') { | 139 | if (*newlist == '\0') { |
111 | fprintf(stderr, "Warning: no --private-bin list executable found, option disabled\n"); | 140 | // fprintf(stderr, "Warning: no --private-bin list executable found, option disabled\n"); |
112 | cfg.bin_private_keep = NULL; | 141 | // cfg.bin_private_keep = NULL; |
113 | arg_private_bin = 0; | 142 | // arg_private_bin = 0; |
114 | free(newlist); | 143 | free(newlist); |
115 | } | 144 | } |
116 | else { | 145 | else { |
117 | ptr = strrchr(newlist, ','); | 146 | ptr = strrchr(newlist, ','); |
118 | assert(ptr); | 147 | assert(ptr); |
119 | *ptr = '\0'; | 148 | *ptr = '\0'; |
120 | if (notfound) | 149 | if (notfound && !arg_quiet) |
121 | fprintf(stderr, "Warning: not all executables from --private-bin list were found. The current list is %s\n", newlist); | 150 | fprintf(stderr, "Warning: not all executables from --private-bin list were found. The current list is %s\n", newlist); |
122 | 151 | ||
123 | cfg.bin_private_keep = newlist; | 152 | cfg.bin_private_keep = newlist; |
@@ -127,7 +156,6 @@ void fs_check_bin_list(void) { | |||
127 | } | 156 | } |
128 | 157 | ||
129 | static void duplicate(char *fname) { | 158 | static void duplicate(char *fname) { |
130 | char *cmd; | ||
131 | char *path = check_dir_or_file(fname); | 159 | char *path = check_dir_or_file(fname); |
132 | if (!path) | 160 | if (!path) |
133 | return; | 161 | return; |
@@ -153,13 +181,24 @@ static void duplicate(char *fname) { | |||
153 | } | 181 | } |
154 | else { | 182 | else { |
155 | // copy the file | 183 | // copy the file |
156 | if (asprintf(&cmd, "%s -a %s %s/%s", RUN_CP_COMMAND, actual_path, RUN_BIN_DIR, fname) == -1) | ||
157 | errExit("asprintf"); | ||
158 | if (arg_debug) | 184 | if (arg_debug) |
159 | printf("%s\n", cmd); | 185 | printf("running: %s -a %s %s/%s", RUN_CP_COMMAND, actual_path, RUN_BIN_DIR, fname); |
160 | if (system(cmd)) | 186 | |
161 | errExit("system cp -a"); | 187 | pid_t child = fork(); |
162 | free(cmd); | 188 | if (child < 0) |
189 | errExit("fork"); | ||
190 | if (child == 0) { | ||
191 | char *f; | ||
192 | if (asprintf(&f, "%s/%s", RUN_BIN_DIR, fname) == -1) | ||
193 | errExit("asprintf"); | ||
194 | clearenv(); | ||
195 | execlp(RUN_CP_COMMAND, RUN_CP_COMMAND, "-a", actual_path, f, NULL); | ||
196 | perror("execlp"); | ||
197 | _exit(1); | ||
198 | } | ||
199 | // wait for the child to finish | ||
200 | waitpid(child, NULL, 0); | ||
201 | |||
163 | } | 202 | } |
164 | free(actual_path); | 203 | free(actual_path); |
165 | } | 204 | } |
@@ -172,29 +211,8 @@ void fs_private_bin_list(void) { | |||
172 | char *private_list = cfg.bin_private_keep; | 211 | char *private_list = cfg.bin_private_keep; |
173 | assert(private_list); | 212 | assert(private_list); |
174 | 213 | ||
175 | // check bin paths | 214 | // create /run/firejail/mnt/bin directory |
176 | int i = 0; | 215 | mkdir_attr(RUN_BIN_DIR, 0755, 0, 0); |
177 | #if 0 | ||
178 | while (paths[i]) { | ||
179 | struct stat s; | ||
180 | if (stat(paths[i], &s) == -1) { | ||
181 | fprintf(stderr, "Error: cannot find %s directory\n", paths[i]); | ||
182 | exit(1); | ||
183 | } | ||
184 | i++; | ||
185 | } | ||
186 | #endif | ||
187 | |||
188 | // create /tmp/firejail/mnt/bin directory | ||
189 | fs_build_mnt_dir(); | ||
190 | int rv = mkdir(RUN_BIN_DIR, 0755); | ||
191 | if (rv == -1) | ||
192 | errExit("mkdir"); | ||
193 | if (chown(RUN_BIN_DIR, 0, 0) < 0) | ||
194 | errExit("chown"); | ||
195 | if (chmod(RUN_BIN_DIR, 0755) < 0) | ||
196 | errExit("chmod"); | ||
197 | |||
198 | 216 | ||
199 | // copy the list of files in the new etc directory | 217 | // copy the list of files in the new etc directory |
200 | // using a new child process without root privileges | 218 | // using a new child process without root privileges |
@@ -225,13 +243,16 @@ void fs_private_bin_list(void) { | |||
225 | duplicate(ptr); | 243 | duplicate(ptr); |
226 | free(dlist); | 244 | free(dlist); |
227 | fs_logger_print(); | 245 | fs_logger_print(); |
228 | exit(0); | 246 | #ifdef HAVE_GCOV |
247 | __gcov_flush(); | ||
248 | #endif | ||
249 | _exit(0); | ||
229 | } | 250 | } |
230 | // wait for the child to finish | 251 | // wait for the child to finish |
231 | waitpid(child, NULL, 0); | 252 | waitpid(child, NULL, 0); |
232 | 253 | ||
233 | // mount-bind | 254 | // mount-bind |
234 | i = 0; | 255 | int i = 0; |
235 | while (paths[i]) { | 256 | while (paths[i]) { |
236 | struct stat s; | 257 | struct stat s; |
237 | if (stat(paths[i], &s) == 0) { | 258 | if (stat(paths[i], &s) == 0) { |
diff --git a/src/firejail/fs_dev.c b/src/firejail/fs_dev.c index 2fd450391..d710e98f2 100644 --- a/src/firejail/fs_dev.c +++ b/src/firejail/fs_dev.c | |||
@@ -30,17 +30,75 @@ | |||
30 | #endif | 30 | #endif |
31 | #include <sys/types.h> | 31 | #include <sys/types.h> |
32 | 32 | ||
33 | typedef struct { | ||
34 | const char *dev_fname; | ||
35 | const char *run_fname; | ||
36 | int sound; | ||
37 | int hw3d; | ||
38 | } DevEntry; | ||
39 | |||
40 | static DevEntry dev[] = { | ||
41 | {"/dev/snd", RUN_DEV_DIR "/snd", 1, 0}, // sound device | ||
42 | {"/dev/dri", RUN_DEV_DIR "/dri", 0, 1}, // 3d device | ||
43 | {"/dev/nvidia0", RUN_DEV_DIR "/nvidia0", 0, 1}, | ||
44 | {"/dev/nvidia1", RUN_DEV_DIR "/nvidia1", 0, 1}, | ||
45 | {"/dev/nvidia2", RUN_DEV_DIR "/nvidia2", 0, 1}, | ||
46 | {"/dev/nvidia3", RUN_DEV_DIR "/nvidia3", 0, 1}, | ||
47 | {"/dev/nvidia4", RUN_DEV_DIR "/nvidia4", 0, 1}, | ||
48 | {"/dev/nvidia5", RUN_DEV_DIR "/nvidia5", 0, 1}, | ||
49 | {"/dev/nvidia6", RUN_DEV_DIR "/nvidia6", 0, 1}, | ||
50 | {"/dev/nvidia7", RUN_DEV_DIR "/nvidia7", 0, 1}, | ||
51 | {"/dev/nvidia8", RUN_DEV_DIR "/nvidia8", 0, 1}, | ||
52 | {"/dev/nvidia9", RUN_DEV_DIR "/nvidia9", 0, 1}, | ||
53 | {"/dev/nvidiactl", RUN_DEV_DIR "/nvidiactl", 0, 1}, | ||
54 | {"/dev/nvidia-modset", RUN_DEV_DIR "/nvidia-modset", 0, 1}, | ||
55 | {"/dev/nvidia-uvm", RUN_DEV_DIR "/nvidia-uvm", 0, 1}, | ||
56 | {NULL, NULL, 0, 0} | ||
57 | }; | ||
58 | |||
59 | static void deventry_mount(void) { | ||
60 | int i = 0; | ||
61 | while (dev[i].dev_fname != NULL) { | ||
62 | struct stat s; | ||
63 | if (stat(dev[i].run_fname, &s) == 0) { | ||
64 | int dir = is_dir(dev[i].run_fname); | ||
65 | if (arg_debug) | ||
66 | printf("mounting %s %s\n", dev[i].run_fname, (dir)? "directory": "file"); | ||
67 | if (dir) { | ||
68 | mkdir_attr(dev[i].dev_fname, 0755, 0, 0); | ||
69 | } | ||
70 | else { | ||
71 | struct stat s; | ||
72 | if (stat(dev[i].run_fname, &s) == -1) { | ||
73 | if (arg_debug) | ||
74 | printf("Warning: cannot stat %s file\n", dev[i].run_fname); | ||
75 | i++; | ||
76 | continue; | ||
77 | } | ||
78 | FILE *fp = fopen(dev[i].dev_fname, "w"); | ||
79 | if (fp) { | ||
80 | fprintf(fp, "\n"); | ||
81 | SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); | ||
82 | fclose(fp); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | if (mount(dev[i].run_fname, dev[i].dev_fname, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
87 | errExit("mounting dev file"); | ||
88 | fs_logger2("whitelist", dev[i].dev_fname); | ||
89 | } | ||
90 | |||
91 | i++; | ||
92 | } | ||
93 | } | ||
94 | |||
33 | static void create_char_dev(const char *path, mode_t mode, int major, int minor) { | 95 | static void create_char_dev(const char *path, mode_t mode, int major, int minor) { |
34 | dev_t dev = makedev(major, minor); | 96 | dev_t dev = makedev(major, minor); |
35 | int rv = mknod(path, S_IFCHR | mode, dev); | 97 | if (mknod(path, S_IFCHR | mode, dev) == -1) |
36 | if (rv == -1) | ||
37 | goto errexit; | 98 | goto errexit; |
38 | |||
39 | |||
40 | if (chmod(path, mode) < 0) | 99 | if (chmod(path, mode) < 0) |
41 | goto errexit; | 100 | goto errexit; |
42 | if (chown(path, 0, 0) < 0) | 101 | ASSERT_PERMS(path, 0, 0, mode); |
43 | goto errexit; | ||
44 | 102 | ||
45 | return; | 103 | return; |
46 | 104 | ||
@@ -62,35 +120,19 @@ errexit: | |||
62 | } | 120 | } |
63 | 121 | ||
64 | void fs_private_dev(void){ | 122 | void fs_private_dev(void){ |
65 | int rv; | ||
66 | // install a new /dev directory | 123 | // install a new /dev directory |
67 | if (arg_debug) | 124 | if (arg_debug) |
68 | printf("Mounting tmpfs on /dev\n"); | 125 | printf("Mounting tmpfs on /dev\n"); |
69 | 126 | ||
70 | int have_dri = 0; | ||
71 | struct stat s; | ||
72 | if (stat("/dev/dri", &s) == 0) | ||
73 | have_dri = 1; | ||
74 | |||
75 | // create DRI_DIR | 127 | // create DRI_DIR |
76 | fs_build_mnt_dir(); | 128 | // keep a copy of dev directory |
77 | if (have_dri) { | 129 | mkdir_attr(RUN_DEV_DIR, 0755, 0, 0); |
78 | /* coverity[toctou] */ | 130 | if (mount("/dev", RUN_DEV_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
79 | rv = mkdir(RUN_DRI_DIR, 0755); | 131 | errExit("mounting /dev/dri"); |
80 | if (rv == -1) | 132 | |
81 | errExit("mkdir"); | 133 | // create DEVLOG_FILE |
82 | if (chown(RUN_DRI_DIR, 0, 0) < 0) | ||
83 | errExit("chown"); | ||
84 | if (chmod(RUN_DRI_DIR, 0755) < 0) | ||
85 | errExit("chmod"); | ||
86 | |||
87 | // keep a copy of /dev/dri under DRI_DIR | ||
88 | if (mount("/dev/dri", RUN_DRI_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
89 | errExit("mounting /dev/dri"); | ||
90 | } | ||
91 | |||
92 | // restore /dev/log | ||
93 | int have_devlog = 0; | 134 | int have_devlog = 0; |
135 | struct stat s; | ||
94 | if (stat("/dev/log", &s) == 0) { | 136 | if (stat("/dev/log", &s) == 0) { |
95 | have_devlog = 1; | 137 | have_devlog = 1; |
96 | FILE *fp = fopen(RUN_DEVLOG_FILE, "w"); | 138 | FILE *fp = fopen(RUN_DEVLOG_FILE, "w"); |
@@ -108,6 +150,8 @@ void fs_private_dev(void){ | |||
108 | if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 150 | if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
109 | errExit("mounting /dev"); | 151 | errExit("mounting /dev"); |
110 | fs_logger("tmpfs /dev"); | 152 | fs_logger("tmpfs /dev"); |
153 | |||
154 | deventry_mount(); | ||
111 | 155 | ||
112 | // bring back /dev/log | 156 | // bring back /dev/log |
113 | if (have_devlog) { | 157 | if (have_devlog) { |
@@ -120,32 +164,14 @@ void fs_private_dev(void){ | |||
120 | fs_logger("clone /dev/log"); | 164 | fs_logger("clone /dev/log"); |
121 | } | 165 | } |
122 | } | 166 | } |
167 | if (mount(RUN_RO_DIR, RUN_DEV_DIR, "none", MS_BIND, "mode=400,gid=0") < 0) | ||
168 | errExit("disable /dev/snd"); | ||
123 | 169 | ||
124 | // bring back the /dev/dri directory | ||
125 | if (have_dri) { | ||
126 | /* coverity[toctou] */ | ||
127 | rv = mkdir("/dev/dri", 0755); | ||
128 | if (rv == -1) | ||
129 | errExit("mkdir"); | ||
130 | if (chown("/dev/dri", 0, 0) < 0) | ||
131 | errExit("chown"); | ||
132 | if (chmod("/dev/dri",0755) < 0) | ||
133 | errExit("chmod"); | ||
134 | if (mount(RUN_DRI_DIR, "/dev/dri", NULL, MS_BIND|MS_REC, NULL) < 0) | ||
135 | errExit("mounting /dev/dri"); | ||
136 | fs_logger("whitelist /dev/dri"); | ||
137 | } | ||
138 | 170 | ||
139 | // create /dev/shm | 171 | // create /dev/shm |
140 | if (arg_debug) | 172 | if (arg_debug) |
141 | printf("Create /dev/shm directory\n"); | 173 | printf("Create /dev/shm directory\n"); |
142 | rv = mkdir("/dev/shm", 01777); | 174 | mkdir_attr("/dev/shm", 01777, 0, 0); |
143 | if (rv == -1) | ||
144 | errExit("mkdir"); | ||
145 | if (chown("/dev/shm", 0, 0) < 0) | ||
146 | errExit("chown"); | ||
147 | if (chmod("/dev/shm", 01777) < 0) | ||
148 | errExit("chmod"); | ||
149 | fs_logger("mkdir /dev/shm"); | 175 | fs_logger("mkdir /dev/shm"); |
150 | 176 | ||
151 | // create devices | 177 | // create devices |
@@ -167,13 +193,7 @@ void fs_private_dev(void){ | |||
167 | #endif | 193 | #endif |
168 | 194 | ||
169 | // pseudo-terminal | 195 | // pseudo-terminal |
170 | rv = mkdir("/dev/pts", 0755); | 196 | mkdir_attr("/dev/pts", 0755, 0, 0); |
171 | if (rv == -1) | ||
172 | errExit("mkdir"); | ||
173 | if (chown("/dev/pts", 0, 0) < 0) | ||
174 | errExit("chown"); | ||
175 | if (chmod("/dev/pts", 0755) < 0) | ||
176 | errExit("chmod"); | ||
177 | fs_logger("mkdir /dev/pts"); | 197 | fs_logger("mkdir /dev/pts"); |
178 | create_char_dev("/dev/pts/ptmx", 0666, 5, 2); //"mknod -m 666 /dev/pts/ptmx c 5 2"); | 198 | create_char_dev("/dev/pts/ptmx", 0666, 5, 2); //"mknod -m 666 /dev/pts/ptmx c 5 2"); |
179 | fs_logger("mknod /dev/pts/ptmx"); | 199 | fs_logger("mknod /dev/pts/ptmx"); |
@@ -186,7 +206,7 @@ void fs_private_dev(void){ | |||
186 | 206 | ||
187 | 207 | ||
188 | // mount /dev/pts | 208 | // mount /dev/pts |
189 | gid_t ttygid = get_tty_gid(); | 209 | gid_t ttygid = get_group_id("tty"); |
190 | char *data; | 210 | char *data; |
191 | if (asprintf(&data, "newinstance,gid=%d,mode=620,ptmxmode=0666", (int) ttygid) == -1) | 211 | if (asprintf(&data, "newinstance,gid=%d,mode=620,ptmxmode=0666", (int) ttygid) == -1) |
192 | errExit("asprintf"); | 212 | errExit("asprintf"); |
@@ -205,6 +225,7 @@ void fs_private_dev(void){ | |||
205 | } | 225 | } |
206 | 226 | ||
207 | 227 | ||
228 | #if 0 | ||
208 | void fs_dev_shm(void) { | 229 | void fs_dev_shm(void) { |
209 | uid_t uid = getuid(); // set a new shm only if we started as root | 230 | uid_t uid = getuid(); // set a new shm only if we started as root |
210 | if (uid) | 231 | if (uid) |
@@ -222,12 +243,7 @@ void fs_dev_shm(void) { | |||
222 | if (lnk) { | 243 | if (lnk) { |
223 | if (!is_dir(lnk)) { | 244 | if (!is_dir(lnk)) { |
224 | // create directory | 245 | // create directory |
225 | if (mkdir(lnk, 01777)) | 246 | mkdir_attr(lnk, 01777, 0, 0); |
226 | errExit("mkdir"); | ||
227 | if (chown(lnk, 0, 0)) | ||
228 | errExit("chown"); | ||
229 | if (chmod(lnk, 01777)) | ||
230 | errExit("chmod"); | ||
231 | } | 247 | } |
232 | if (arg_debug) | 248 | if (arg_debug) |
233 | printf("Mounting tmpfs on %s on behalf of /dev/shm\n", lnk); | 249 | printf("Mounting tmpfs on %s on behalf of /dev/shm\n", lnk); |
@@ -243,3 +259,40 @@ void fs_dev_shm(void) { | |||
243 | 259 | ||
244 | } | 260 | } |
245 | } | 261 | } |
262 | #endif | ||
263 | |||
264 | static void disable_file_or_dir(const char *fname) { | ||
265 | if (arg_debug) | ||
266 | printf("disable %s\n", fname); | ||
267 | struct stat s; | ||
268 | if (stat(fname, &s) != -1) { | ||
269 | if (is_dir(fname)) { | ||
270 | if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) | ||
271 | errExit("disable directory"); | ||
272 | } | ||
273 | else { | ||
274 | if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) | ||
275 | errExit("disable file"); | ||
276 | } | ||
277 | } | ||
278 | fs_logger2("blacklist", fname); | ||
279 | |||
280 | } | ||
281 | |||
282 | void fs_dev_disable_sound(void) { | ||
283 | int i = 0; | ||
284 | while (dev[i].dev_fname != NULL) { | ||
285 | if (dev[i].sound) | ||
286 | disable_file_or_dir(dev[i].dev_fname); | ||
287 | i++; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | void fs_dev_disable_3d(void) { | ||
292 | int i = 0; | ||
293 | while (dev[i].dev_fname != NULL) { | ||
294 | if (dev[i].hw3d) | ||
295 | disable_file_or_dir(dev[i].dev_fname); | ||
296 | i++; | ||
297 | } | ||
298 | } | ||
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c index 1a44b1305..7e18840fd 100644 --- a/src/firejail/fs_etc.c +++ b/src/firejail/fs_etc.c | |||
@@ -28,7 +28,7 @@ | |||
28 | static int check_dir_or_file(const char *name) { | 28 | static int check_dir_or_file(const char *name) { |
29 | assert(name); | 29 | assert(name); |
30 | invalid_filename(name); | 30 | invalid_filename(name); |
31 | 31 | ||
32 | struct stat s; | 32 | struct stat s; |
33 | char *fname; | 33 | char *fname; |
34 | if (asprintf(&fname, "/etc/%s", name) == -1) | 34 | if (asprintf(&fname, "/etc/%s", name) == -1) |
@@ -40,7 +40,11 @@ static int check_dir_or_file(const char *name) { | |||
40 | printf("Warning: file %s not found.\n", fname); | 40 | printf("Warning: file %s not found.\n", fname); |
41 | return 0; | 41 | return 0; |
42 | } | 42 | } |
43 | 43 | ||
44 | // read access | ||
45 | if (access(fname, R_OK) == -1) | ||
46 | goto errexit; | ||
47 | |||
44 | // dir or regular file | 48 | // dir or regular file |
45 | if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { | 49 | if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { |
46 | free(fname); | 50 | free(fname); |
@@ -52,6 +56,8 @@ static int check_dir_or_file(const char *name) { | |||
52 | return 1; | 56 | return 1; |
53 | } | 57 | } |
54 | 58 | ||
59 | |||
60 | errexit: | ||
55 | fprintf(stderr, "Error: invalid file type, %s.\n", fname); | 61 | fprintf(stderr, "Error: invalid file type, %s.\n", fname); |
56 | exit(1); | 62 | exit(1); |
57 | } | 63 | } |
@@ -88,18 +94,25 @@ void fs_check_etc_list(void) { | |||
88 | } | 94 | } |
89 | 95 | ||
90 | static void duplicate(char *fname) { | 96 | static void duplicate(char *fname) { |
91 | char *cmd; | 97 | // copy the file |
92 | |||
93 | // copy the file - this code assumes ETC_DIR is actually MNT_DIR/etc | ||
94 | if (asprintf(&cmd, "%s -a --parents /etc/%s %s", RUN_CP_COMMAND, fname, RUN_MNT_DIR) == -1) | ||
95 | errExit("asprintf"); | ||
96 | if (arg_debug) | 98 | if (arg_debug) |
97 | printf("%s\n", cmd); | 99 | printf("running: %s -a --parents /etc/%s %s\n", RUN_CP_COMMAND, fname, RUN_MNT_DIR); |
98 | if (system(cmd)) | 100 | |
99 | fprintf(stderr, "Warning (fs_etc): error copying file /etc/%s, skipping...\n", fname); | 101 | pid_t child = fork(); |
102 | if (child < 0) | ||
103 | errExit("fork"); | ||
104 | if (child == 0) { | ||
105 | char *f; | ||
106 | if (asprintf(&f, "/etc/%s", fname) == -1) | ||
107 | errExit("asprintf"); | ||
108 | clearenv(); | ||
109 | execlp(RUN_CP_COMMAND, RUN_CP_COMMAND, "-a", "--parents", f, RUN_MNT_DIR, NULL); | ||
110 | perror("execlp"); | ||
111 | _exit(1); | ||
112 | } | ||
113 | // wait for the child to finish | ||
114 | waitpid(child, NULL, 0); | ||
100 | 115 | ||
101 | free(cmd); | ||
102 | |||
103 | char *name; | 116 | char *name; |
104 | if (asprintf(&name, "/etc/%s", fname) == -1) | 117 | if (asprintf(&name, "/etc/%s", fname) == -1) |
105 | errExit("asprintf"); | 118 | errExit("asprintf"); |
@@ -118,51 +131,51 @@ void fs_private_etc_list(void) { | |||
118 | exit(1); | 131 | exit(1); |
119 | } | 132 | } |
120 | 133 | ||
121 | // create /tmp/firejail/mnt/etc directory | 134 | // create /run/firejail/mnt/etc directory |
122 | fs_build_mnt_dir(); | 135 | mkdir_attr(RUN_ETC_DIR, 0755, 0, 0); |
123 | int rv = mkdir(RUN_ETC_DIR, 0755); | ||
124 | if (rv == -1) | ||
125 | errExit("mkdir"); | ||
126 | if (chown(RUN_ETC_DIR, 0, 0) < 0) | ||
127 | errExit("chown"); | ||
128 | if (chmod(RUN_ETC_DIR, 0755) < 0) | ||
129 | errExit("chmod"); | ||
130 | fs_logger("tmpfs /etc"); | 136 | fs_logger("tmpfs /etc"); |
131 | 137 | ||
132 | // copy the list of files in the new etc directory | ||
133 | // using a new child process without root privileges | ||
134 | fs_logger_print(); // save the current log | 138 | fs_logger_print(); // save the current log |
135 | pid_t child = fork(); | ||
136 | if (child < 0) | ||
137 | errExit("fork"); | ||
138 | if (child == 0) { | ||
139 | if (arg_debug) | ||
140 | printf("Copying files in the new etc directory:\n"); | ||
141 | 139 | ||
142 | // elevate privileges - files in the new /etc directory belong to root | ||
143 | if (setreuid(0, 0) < 0) | ||
144 | errExit("setreuid"); | ||
145 | if (setregid(0, 0) < 0) | ||
146 | errExit("setregid"); | ||
147 | |||
148 | // copy the list of files in the new home directory | ||
149 | char *dlist = strdup(private_list); | ||
150 | if (!dlist) | ||
151 | errExit("strdup"); | ||
152 | |||
153 | 140 | ||
154 | char *ptr = strtok(dlist, ","); | 141 | // copy the list of files in the new etc directory |
155 | duplicate(ptr); | 142 | // using a new child process with root privileges |
143 | if (*private_list != '\0') { | ||
144 | pid_t child = fork(); | ||
145 | if (child < 0) | ||
146 | errExit("fork"); | ||
147 | if (child == 0) { | ||
148 | if (arg_debug) | ||
149 | printf("Copying files in the new etc directory:\n"); | ||
156 | 150 | ||
157 | while ((ptr = strtok(NULL, ",")) != NULL) | 151 | // elevate privileges - files in the new /etc directory belong to root |
152 | if (setreuid(0, 0) < 0) | ||
153 | errExit("setreuid"); | ||
154 | if (setregid(0, 0) < 0) | ||
155 | errExit("setregid"); | ||
156 | |||
157 | // copy the list of files in the new home directory | ||
158 | char *dlist = strdup(private_list); | ||
159 | if (!dlist) | ||
160 | errExit("strdup"); | ||
161 | |||
162 | |||
163 | char *ptr = strtok(dlist, ","); | ||
158 | duplicate(ptr); | 164 | duplicate(ptr); |
159 | free(dlist); | 165 | |
160 | fs_logger_print(); | 166 | while ((ptr = strtok(NULL, ",")) != NULL) |
161 | exit(0); | 167 | duplicate(ptr); |
168 | free(dlist); | ||
169 | fs_logger_print(); | ||
170 | #ifdef HAVE_GCOV | ||
171 | __gcov_flush(); | ||
172 | #endif | ||
173 | _exit(0); | ||
174 | } | ||
175 | // wait for the child to finish | ||
176 | waitpid(child, NULL, 0); | ||
162 | } | 177 | } |
163 | // wait for the child to finish | 178 | |
164 | waitpid(child, NULL, 0); | ||
165 | |||
166 | if (arg_debug) | 179 | if (arg_debug) |
167 | printf("Mount-bind %s on top of /etc\n", RUN_ETC_DIR); | 180 | printf("Mount-bind %s on top of /etc\n", RUN_ETC_DIR); |
168 | if (mount(RUN_ETC_DIR, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) | 181 | if (mount(RUN_ETC_DIR, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) |
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index d4a16da0a..242482d26 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -28,11 +28,13 @@ | |||
28 | #include <sys/wait.h> | 28 | #include <sys/wait.h> |
29 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #include <grp.h> | 30 | #include <grp.h> |
31 | #include <ftw.h> | ||
31 | 32 | ||
32 | static void skel(const char *homedir, uid_t u, gid_t g) { | 33 | static void skel(const char *homedir, uid_t u, gid_t g) { |
33 | char *fname; | 34 | char *fname; |
35 | |||
34 | // zsh | 36 | // zsh |
35 | if (arg_zsh) { | 37 | if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) { |
36 | // copy skel files | 38 | // copy skel files |
37 | if (asprintf(&fname, "%s/.zshrc", homedir) == -1) | 39 | if (asprintf(&fname, "%s/.zshrc", homedir) == -1) |
38 | errExit("asprintf"); | 40 | errExit("asprintf"); |
@@ -41,13 +43,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
41 | if (stat(fname, &s) == 0) | 43 | if (stat(fname, &s) == 0) |
42 | return; | 44 | return; |
43 | if (stat("/etc/skel/.zshrc", &s) == 0) { | 45 | if (stat("/etc/skel/.zshrc", &s) == 0) { |
44 | if (is_link("/etc/skel/.zshrc")) { | 46 | if (copy_file("/etc/skel/.zshrc", fname, u, g, 0644) == 0) { |
45 | fprintf(stderr, "Error: invalid /etc/skel/.zshrc file\n"); | ||
46 | exit(1); | ||
47 | } | ||
48 | if (copy_file("/etc/skel/.zshrc", fname) == 0) { | ||
49 | if (chown(fname, u, g) == -1) | ||
50 | errExit("chown"); | ||
51 | fs_logger("clone /etc/skel/.zshrc"); | 47 | fs_logger("clone /etc/skel/.zshrc"); |
52 | } | 48 | } |
53 | } | 49 | } |
@@ -55,18 +51,15 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
55 | FILE *fp = fopen(fname, "w"); | 51 | FILE *fp = fopen(fname, "w"); |
56 | if (fp) { | 52 | if (fp) { |
57 | fprintf(fp, "\n"); | 53 | fprintf(fp, "\n"); |
54 | SET_PERMS_STREAM(fp, u, g, S_IRUSR | S_IWUSR); | ||
58 | fclose(fp); | 55 | fclose(fp); |
59 | if (chown(fname, u, g) == -1) | ||
60 | errExit("chown"); | ||
61 | if (chmod(fname, S_IRUSR | S_IWUSR) < 0) | ||
62 | errExit("chown"); | ||
63 | fs_logger2("touch", fname); | 56 | fs_logger2("touch", fname); |
64 | } | 57 | } |
65 | } | 58 | } |
66 | free(fname); | 59 | free(fname); |
67 | } | 60 | } |
68 | // csh | 61 | // csh |
69 | else if (arg_csh) { | 62 | else if (!arg_shell_none && strcmp(cfg.shell,"/bin/csh") == 0) { |
70 | // copy skel files | 63 | // copy skel files |
71 | if (asprintf(&fname, "%s/.cshrc", homedir) == -1) | 64 | if (asprintf(&fname, "%s/.cshrc", homedir) == -1) |
72 | errExit("asprintf"); | 65 | errExit("asprintf"); |
@@ -75,13 +68,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
75 | if (stat(fname, &s) == 0) | 68 | if (stat(fname, &s) == 0) |
76 | return; | 69 | return; |
77 | if (stat("/etc/skel/.cshrc", &s) == 0) { | 70 | if (stat("/etc/skel/.cshrc", &s) == 0) { |
78 | if (is_link("/etc/skel/.cshrc")) { | 71 | if (copy_file("/etc/skel/.cshrc", fname, u, g, 0644) == 0) { |
79 | fprintf(stderr, "Error: invalid /etc/skel/.cshrc file\n"); | ||
80 | exit(1); | ||
81 | } | ||
82 | if (copy_file("/etc/skel/.cshrc", fname) == 0) { | ||
83 | if (chown(fname, u, g) == -1) | ||
84 | errExit("chown"); | ||
85 | fs_logger("clone /etc/skel/.cshrc"); | 72 | fs_logger("clone /etc/skel/.cshrc"); |
86 | } | 73 | } |
87 | } | 74 | } |
@@ -90,11 +77,8 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
90 | FILE *fp = fopen(fname, "w"); | 77 | FILE *fp = fopen(fname, "w"); |
91 | if (fp) { | 78 | if (fp) { |
92 | fprintf(fp, "\n"); | 79 | fprintf(fp, "\n"); |
80 | SET_PERMS_STREAM(fp, u, g, S_IRUSR | S_IWUSR); | ||
93 | fclose(fp); | 81 | fclose(fp); |
94 | if (chown(fname, u, g) == -1) | ||
95 | errExit("chown"); | ||
96 | if (chmod(fname, S_IRUSR | S_IWUSR) < 0) | ||
97 | errExit("chown"); | ||
98 | fs_logger2("touch", fname); | 82 | fs_logger2("touch", fname); |
99 | } | 83 | } |
100 | } | 84 | } |
@@ -110,14 +94,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
110 | if (stat(fname, &s) == 0) | 94 | if (stat(fname, &s) == 0) |
111 | return; | 95 | return; |
112 | if (stat("/etc/skel/.bashrc", &s) == 0) { | 96 | if (stat("/etc/skel/.bashrc", &s) == 0) { |
113 | if (is_link("/etc/skel/.bashrc")) { | 97 | if (copy_file("/etc/skel/.bashrc", fname, u, g, 0644) == 0) { |
114 | fprintf(stderr, "Error: invalid /etc/skel/.bashrc file\n"); | ||
115 | exit(1); | ||
116 | } | ||
117 | if (copy_file("/etc/skel/.bashrc", fname) == 0) { | ||
118 | /* coverity[toctou] */ | ||
119 | if (chown(fname, u, g) == -1) | ||
120 | errExit("chown"); | ||
121 | fs_logger("clone /etc/skel/.bashrc"); | 98 | fs_logger("clone /etc/skel/.bashrc"); |
122 | } | 99 | } |
123 | } | 100 | } |
@@ -127,8 +104,6 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
127 | 104 | ||
128 | static int store_xauthority(void) { | 105 | static int store_xauthority(void) { |
129 | // put a copy of .Xauthority in XAUTHORITY_FILE | 106 | // put a copy of .Xauthority in XAUTHORITY_FILE |
130 | fs_build_mnt_dir(); | ||
131 | |||
132 | char *src; | 107 | char *src; |
133 | char *dest = RUN_XAUTHORITY_FILE; | 108 | char *dest = RUN_XAUTHORITY_FILE; |
134 | if (asprintf(&src, "%s/.Xauthority", cfg.homedir) == -1) | 109 | if (asprintf(&src, "%s/.Xauthority", cfg.homedir) == -1) |
@@ -137,11 +112,11 @@ static int store_xauthority(void) { | |||
137 | struct stat s; | 112 | struct stat s; |
138 | if (stat(src, &s) == 0) { | 113 | if (stat(src, &s) == 0) { |
139 | if (is_link(src)) { | 114 | if (is_link(src)) { |
140 | fprintf(stderr, "Error: invalid .Xauthority file\n"); | 115 | fprintf(stderr, "Warning: invalid .Xauthority file\n"); |
141 | exit(1); | 116 | return 0; |
142 | } | 117 | } |
143 | 118 | ||
144 | int rv = copy_file(src, dest); | 119 | int rv = copy_file(src, dest, -1, -1, 0600); |
145 | if (rv) { | 120 | if (rv) { |
146 | fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n"); | 121 | fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n"); |
147 | return 0; | 122 | return 0; |
@@ -153,9 +128,6 @@ static int store_xauthority(void) { | |||
153 | } | 128 | } |
154 | 129 | ||
155 | static int store_asoundrc(void) { | 130 | static int store_asoundrc(void) { |
156 | // put a copy of .Xauthority in XAUTHORITY_FILE | ||
157 | fs_build_mnt_dir(); | ||
158 | |||
159 | char *src; | 131 | char *src; |
160 | char *dest = RUN_ASOUNDRC_FILE; | 132 | char *dest = RUN_ASOUNDRC_FILE; |
161 | if (asprintf(&src, "%s/.asoundrc", cfg.homedir) == -1) | 133 | if (asprintf(&src, "%s/.asoundrc", cfg.homedir) == -1) |
@@ -177,7 +149,7 @@ static int store_asoundrc(void) { | |||
177 | free(rp); | 149 | free(rp); |
178 | } | 150 | } |
179 | 151 | ||
180 | int rv = copy_file(src, dest); | 152 | int rv = copy_file(src, dest, -1, -1, -0644); |
181 | if (rv) { | 153 | if (rv) { |
182 | fprintf(stderr, "Warning: cannot transfer .asoundrc in private home directory\n"); | 154 | fprintf(stderr, "Warning: cannot transfer .asoundrc in private home directory\n"); |
183 | return 0; | 155 | return 0; |
@@ -194,17 +166,12 @@ static void copy_xauthority(void) { | |||
194 | char *dest; | 166 | char *dest; |
195 | if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) | 167 | if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) |
196 | errExit("asprintf"); | 168 | errExit("asprintf"); |
197 | int rv = copy_file(src, dest); | 169 | // copy, set permissions and ownership |
170 | int rv = copy_file(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); | ||
198 | if (rv) | 171 | if (rv) |
199 | fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n"); | 172 | fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n"); |
200 | else { | 173 | else { |
201 | fs_logger2("clone", dest); | 174 | fs_logger2("clone", dest); |
202 | |||
203 | // set permissions and ownership | ||
204 | if (chown(dest, getuid(), getgid()) < 0) | ||
205 | errExit("chown"); | ||
206 | if (chmod(dest, S_IRUSR | S_IWUSR) < 0) | ||
207 | errExit("chmod"); | ||
208 | } | 175 | } |
209 | 176 | ||
210 | // delete the temporary file | 177 | // delete the temporary file |
@@ -217,17 +184,12 @@ static void copy_asoundrc(void) { | |||
217 | char *dest; | 184 | char *dest; |
218 | if (asprintf(&dest, "%s/.asoundrc", cfg.homedir) == -1) | 185 | if (asprintf(&dest, "%s/.asoundrc", cfg.homedir) == -1) |
219 | errExit("asprintf"); | 186 | errExit("asprintf"); |
220 | int rv = copy_file(src, dest); | 187 | // copy, set permissions and ownership |
188 | int rv = copy_file(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); | ||
221 | if (rv) | 189 | if (rv) |
222 | fprintf(stderr, "Warning: cannot transfer .asoundrc in private home directory\n"); | 190 | fprintf(stderr, "Warning: cannot transfer .asoundrc in private home directory\n"); |
223 | else { | 191 | else { |
224 | fs_logger2("clone", dest); | 192 | fs_logger2("clone", dest); |
225 | |||
226 | // set permissions and ownership | ||
227 | if (chown(dest, getuid(), getgid()) < 0) | ||
228 | errExit("chown"); | ||
229 | if (chmod(dest, S_IRUSR | S_IWUSR) < 0) | ||
230 | errExit("chmod"); | ||
231 | } | 193 | } |
232 | 194 | ||
233 | // delete the temporary file | 195 | // delete the temporary file |
@@ -260,7 +222,7 @@ void fs_private_homedir(void) { | |||
260 | // mount bind private_homedir on top of homedir | 222 | // mount bind private_homedir on top of homedir |
261 | if (arg_debug) | 223 | if (arg_debug) |
262 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); | 224 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); |
263 | if (mount(private_homedir, homedir, NULL, MS_BIND|MS_REC, NULL) < 0) | 225 | if (mount(private_homedir, homedir, NULL, MS_NOSUID | MS_NODEV | MS_BIND | MS_REC, NULL) < 0) |
264 | errExit("mount bind"); | 226 | errExit("mount bind"); |
265 | fs_logger3("mount-bind", private_homedir, cfg.homedir); | 227 | fs_logger3("mount-bind", private_homedir, cfg.homedir); |
266 | fs_logger2("whitelist", cfg.homedir); | 228 | fs_logger2("whitelist", cfg.homedir); |
@@ -274,7 +236,7 @@ void fs_private_homedir(void) { | |||
274 | // mask /root | 236 | // mask /root |
275 | if (arg_debug) | 237 | if (arg_debug) |
276 | printf("Mounting a new /root directory\n"); | 238 | printf("Mounting a new /root directory\n"); |
277 | if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) | 239 | if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) |
278 | errExit("mounting home directory"); | 240 | errExit("mounting home directory"); |
279 | fs_logger("tmpfs /root"); | 241 | fs_logger("tmpfs /root"); |
280 | } | 242 | } |
@@ -282,7 +244,7 @@ void fs_private_homedir(void) { | |||
282 | // mask /home | 244 | // mask /home |
283 | if (arg_debug) | 245 | if (arg_debug) |
284 | printf("Mounting a new /home directory\n"); | 246 | printf("Mounting a new /home directory\n"); |
285 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 247 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
286 | errExit("mounting home directory"); | 248 | errExit("mounting home directory"); |
287 | fs_logger("tmpfs /home"); | 249 | fs_logger("tmpfs /home"); |
288 | } | 250 | } |
@@ -312,14 +274,14 @@ void fs_private(void) { | |||
312 | // mask /home | 274 | // mask /home |
313 | if (arg_debug) | 275 | if (arg_debug) |
314 | printf("Mounting a new /home directory\n"); | 276 | printf("Mounting a new /home directory\n"); |
315 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 277 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
316 | errExit("mounting home directory"); | 278 | errExit("mounting home directory"); |
317 | fs_logger("tmpfs /home"); | 279 | fs_logger("tmpfs /home"); |
318 | 280 | ||
319 | // mask /root | 281 | // mask /root |
320 | if (arg_debug) | 282 | if (arg_debug) |
321 | printf("Mounting a new /root directory\n"); | 283 | printf("Mounting a new /root directory\n"); |
322 | if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) | 284 | if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) |
323 | errExit("mounting root directory"); | 285 | errExit("mounting root directory"); |
324 | fs_logger("tmpfs /root"); | 286 | fs_logger("tmpfs /root"); |
325 | 287 | ||
@@ -343,8 +305,8 @@ void fs_private(void) { | |||
343 | copy_xauthority(); | 305 | copy_xauthority(); |
344 | if (aflag) | 306 | if (aflag) |
345 | copy_asoundrc(); | 307 | copy_asoundrc(); |
346 | } | ||
347 | 308 | ||
309 | } | ||
348 | 310 | ||
349 | // check new private home directory (--private= option) - exit if it fails | 311 | // check new private home directory (--private= option) - exit if it fails |
350 | void fs_check_private_dir(void) { | 312 | void fs_check_private_dir(void) { |
@@ -384,3 +346,318 @@ void fs_check_private_dir(void) { | |||
384 | } | 346 | } |
385 | } | 347 | } |
386 | 348 | ||
349 | //*********************************************************************************** | ||
350 | // --private-home | ||
351 | //*********************************************************************************** | ||
352 | #define PRIVATE_COPY_LIMIT (500 * 1024 *1024) | ||
353 | static int size_limit_reached = 0; | ||
354 | static unsigned file_cnt = 0; | ||
355 | static unsigned size_cnt = 0; | ||
356 | static char *check_dir_or_file(const char *name); | ||
357 | |||
358 | int fs_copydir(const char *path, const struct stat *st, int ftype, struct FTW *sftw) { | ||
359 | (void) st; | ||
360 | (void) sftw; | ||
361 | if (size_limit_reached) | ||
362 | return 0; | ||
363 | |||
364 | struct stat s; | ||
365 | char *dest; | ||
366 | if (asprintf(&dest, "%s%s", RUN_HOME_DIR, path + strlen(cfg.homedir)) == -1) | ||
367 | errExit("asprintf"); | ||
368 | |||
369 | // don't copy it if we already have the file | ||
370 | if (stat(dest, &s) == 0) { | ||
371 | free(dest); | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | // extract mode and ownership | ||
376 | if (stat(path, &s) != 0) { | ||
377 | free(dest); | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | // check uid | ||
382 | if (s.st_uid != firejail_uid || s.st_gid != firejail_gid) { | ||
383 | free(dest); | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | if ((s.st_size + size_cnt) > PRIVATE_COPY_LIMIT) { | ||
388 | size_limit_reached = 1; | ||
389 | free(dest); | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | file_cnt++; | ||
394 | size_cnt += s.st_size; | ||
395 | |||
396 | if(ftype == FTW_F) | ||
397 | copy_file(path, dest, firejail_uid, firejail_gid, s.st_mode); | ||
398 | else if (ftype == FTW_D) { | ||
399 | if (mkdir(dest, s.st_mode) == -1) | ||
400 | errExit("mkdir"); | ||
401 | if (set_perms(dest, firejail_uid, firejail_gid, s.st_mode)) | ||
402 | errExit("set_perms"); | ||
403 | #if 0 | ||
404 | struct stat s2; | ||
405 | if (stat(dest, &s2) == 0) { | ||
406 | printf("%s\t", dest); | ||
407 | printf((S_ISDIR(s.st_mode)) ? "d" : "-"); | ||
408 | printf((s.st_mode & S_IRUSR) ? "r" : "-"); | ||
409 | printf((s.st_mode & S_IWUSR) ? "w" : "-"); | ||
410 | printf((s.st_mode & S_IXUSR) ? "x" : "-"); | ||
411 | printf((s.st_mode & S_IRGRP) ? "r" : "-"); | ||
412 | printf((s.st_mode & S_IWGRP) ? "w" : "-"); | ||
413 | printf((s.st_mode & S_IXGRP) ? "x" : "-"); | ||
414 | printf((s.st_mode & S_IROTH) ? "r" : "-"); | ||
415 | printf((s.st_mode & S_IWOTH) ? "w" : "-"); | ||
416 | printf((s.st_mode & S_IXOTH) ? "x" : "-"); | ||
417 | printf("\n"); | ||
418 | } | ||
419 | #endif | ||
420 | |||
421 | fs_logger2("clone", path); | ||
422 | } | ||
423 | |||
424 | free(dest); | ||
425 | return(0); | ||
426 | } | ||
427 | |||
428 | static void duplicate(char *name) { | ||
429 | char *fname = check_dir_or_file(name); | ||
430 | |||
431 | if (arg_debug) | ||
432 | printf("Private home: duplicating %s\n", fname); | ||
433 | assert(strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0); | ||
434 | |||
435 | struct stat s; | ||
436 | if (stat(fname, &s) == -1) { | ||
437 | free(fname); | ||
438 | return; | ||
439 | } | ||
440 | |||
441 | if(nftw(fname, fs_copydir, 1, FTW_PHYS) != 0) { | ||
442 | fprintf(stderr, "Error: unable to copy template dir\n"); | ||
443 | exit(1); | ||
444 | } | ||
445 | fs_logger_print(); // save the current log | ||
446 | |||
447 | free(fname); | ||
448 | } | ||
449 | |||
450 | |||
451 | |||
452 | static char *check_dir_or_file(const char *name) { | ||
453 | assert(name); | ||
454 | struct stat s; | ||
455 | |||
456 | // basic checks | ||
457 | invalid_filename(name); | ||
458 | |||
459 | if (arg_debug) | ||
460 | printf("Private home: checking %s\n", name); | ||
461 | |||
462 | // expand home directory | ||
463 | char *fname = expand_home(name, cfg.homedir); | ||
464 | if (!fname) { | ||
465 | fprintf(stderr, "Error: file %s not found.\n", name); | ||
466 | exit(1); | ||
467 | } | ||
468 | |||
469 | // If it doesn't start with '/', it must be relative to homedir | ||
470 | if (fname[0] != '/') { | ||
471 | char* tmp; | ||
472 | if (asprintf(&tmp, "%s/%s", cfg.homedir, fname) == -1) | ||
473 | errExit("asprintf"); | ||
474 | free(fname); | ||
475 | fname = tmp; | ||
476 | } | ||
477 | |||
478 | // check the file is in user home directory | ||
479 | char *rname = realpath(fname, NULL); | ||
480 | if (!rname) { | ||
481 | fprintf(stderr, "Error: invalid file %s\n", name); | ||
482 | exit(1); | ||
483 | } | ||
484 | if (strncmp(rname, cfg.homedir, strlen(cfg.homedir)) != 0) { | ||
485 | fprintf(stderr, "Error: file %s is not in user home directory\n", name); | ||
486 | exit(1); | ||
487 | } | ||
488 | |||
489 | // a full home directory is not allowed | ||
490 | if (strcmp(rname, cfg.homedir) == 0) { | ||
491 | fprintf(stderr, "Error: invalid directory %s\n", rname); | ||
492 | exit(1); | ||
493 | } | ||
494 | |||
495 | // only top files and directories in user home are allowed | ||
496 | char *ptr = rname + strlen(cfg.homedir); | ||
497 | if (*ptr == '\0') { | ||
498 | fprintf(stderr, "Error: invalid file %s\n", name); | ||
499 | exit(1); | ||
500 | } | ||
501 | ptr++; | ||
502 | ptr = strchr(ptr, '/'); | ||
503 | if (ptr) { | ||
504 | if (*ptr != '\0') { | ||
505 | fprintf(stderr, "Error: only top files and directories in user home are allowed\n"); | ||
506 | exit(1); | ||
507 | } | ||
508 | } | ||
509 | |||
510 | if (stat(fname, &s) == -1) { | ||
511 | fprintf(stderr, "Error: file %s not found.\n", fname); | ||
512 | exit(1); | ||
513 | } | ||
514 | |||
515 | // check uid | ||
516 | uid_t uid = getuid(); | ||
517 | gid_t gid = getgid(); | ||
518 | if (s.st_uid != uid || s.st_gid != gid) { | ||
519 | fprintf(stderr, "Error: only files or directories created by the current user are allowed.\n"); | ||
520 | exit(1); | ||
521 | } | ||
522 | |||
523 | // dir or regular file | ||
524 | if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { | ||
525 | free(fname); | ||
526 | return rname; // regular exit from the function | ||
527 | } | ||
528 | |||
529 | fprintf(stderr, "Error: invalid file type, %s.\n", fname); | ||
530 | exit(1); | ||
531 | } | ||
532 | |||
533 | |||
534 | // check directory list specified by user (--private-home option) - exit if it fails | ||
535 | void fs_check_home_list(void) { | ||
536 | if (strstr(cfg.home_private_keep, "..")) { | ||
537 | fprintf(stderr, "Error: invalid private-home list\n"); | ||
538 | exit(1); | ||
539 | } | ||
540 | |||
541 | char *dlist = strdup(cfg.home_private_keep); | ||
542 | if (!dlist) | ||
543 | errExit("strdup"); | ||
544 | |||
545 | char *ptr = strtok(dlist, ","); | ||
546 | char *tmp = check_dir_or_file(ptr); | ||
547 | free(tmp); | ||
548 | |||
549 | while ((ptr = strtok(NULL, ",")) != NULL) { | ||
550 | tmp = check_dir_or_file(ptr); | ||
551 | free(tmp); | ||
552 | } | ||
553 | |||
554 | free(dlist); | ||
555 | } | ||
556 | |||
557 | |||
558 | |||
559 | // private mode (--private-home=list): | ||
560 | // mount homedir on top of /home/user, | ||
561 | // tmpfs on top of /root in nonroot mode, | ||
562 | // tmpfs on top of /tmp in root mode, | ||
563 | // set skel files, | ||
564 | // restore .Xauthority | ||
565 | void fs_private_home_list(void) { | ||
566 | char *homedir = cfg.homedir; | ||
567 | char *private_list = cfg.home_private_keep; | ||
568 | assert(homedir); | ||
569 | assert(private_list); | ||
570 | |||
571 | int xflag = store_xauthority(); | ||
572 | int aflag = store_asoundrc(); | ||
573 | |||
574 | uid_t u = firejail_uid; | ||
575 | gid_t g = firejail_gid; | ||
576 | struct stat s; | ||
577 | if (stat(homedir, &s) == -1) { | ||
578 | fprintf(stderr, "Error: cannot find user home directory\n"); | ||
579 | exit(1); | ||
580 | } | ||
581 | |||
582 | // create /run/firejail/mnt/home directory | ||
583 | int rv = mkdir(RUN_HOME_DIR, 0755); | ||
584 | if (rv == -1) | ||
585 | errExit("mkdir"); | ||
586 | if (set_perms(RUN_HOME_DIR, u, g, 0755)) | ||
587 | errExit("set_perms"); | ||
588 | ASSERT_PERMS(RUN_HOME_DIR, u, g, 0755); | ||
589 | |||
590 | fs_logger_print(); // save the current log | ||
591 | |||
592 | // copy the list of files in the new home directory | ||
593 | // using a new child process without root privileges | ||
594 | pid_t child = fork(); | ||
595 | if (child < 0) | ||
596 | errExit("fork"); | ||
597 | if (child == 0) { | ||
598 | if (arg_debug) | ||
599 | printf("Copying files in the new home:\n"); | ||
600 | |||
601 | // drop privileges | ||
602 | if (setgroups(0, NULL) < 0) | ||
603 | errExit("setgroups"); | ||
604 | if (setgid(getgid()) < 0) | ||
605 | errExit("setgid/getgid"); | ||
606 | if (setuid(getuid()) < 0) | ||
607 | errExit("setuid/getuid"); | ||
608 | |||
609 | // copy the list of files in the new home directory | ||
610 | char *dlist = strdup(cfg.home_private_keep); | ||
611 | if (!dlist) | ||
612 | errExit("strdup"); | ||
613 | |||
614 | char *ptr = strtok(dlist, ","); | ||
615 | duplicate(ptr); | ||
616 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
617 | duplicate(ptr); | ||
618 | |||
619 | if (!arg_quiet) { | ||
620 | if (size_limit_reached) | ||
621 | fprintf(stderr, "Warning: private-home copy limit of %u MB reached, not all the files were copied\n", | ||
622 | PRIVATE_COPY_LIMIT / (1024 *1024)); | ||
623 | else | ||
624 | printf("Private home: %u files, total size %u bytes\n", file_cnt, size_cnt); | ||
625 | } | ||
626 | |||
627 | fs_logger_print(); // save the current log | ||
628 | free(dlist); | ||
629 | #ifdef HAVE_GCOV | ||
630 | __gcov_flush(); | ||
631 | #endif | ||
632 | _exit(0); | ||
633 | } | ||
634 | // wait for the child to finish | ||
635 | waitpid(child, NULL, 0); | ||
636 | |||
637 | if (arg_debug) | ||
638 | printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir); | ||
639 | |||
640 | if (mount(RUN_HOME_DIR, homedir, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
641 | errExit("mount bind"); | ||
642 | |||
643 | if (u != 0) { | ||
644 | // mask /root | ||
645 | if (arg_debug) | ||
646 | printf("Mounting a new /root directory\n"); | ||
647 | if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) | ||
648 | errExit("mounting home directory"); | ||
649 | } | ||
650 | else { | ||
651 | // mask /home | ||
652 | if (arg_debug) | ||
653 | printf("Mounting a new /home directory\n"); | ||
654 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
655 | errExit("mounting home directory"); | ||
656 | } | ||
657 | |||
658 | skel(homedir, u, g); | ||
659 | if (xflag) | ||
660 | copy_xauthority(); | ||
661 | if (aflag) | ||
662 | copy_asoundrc(); | ||
663 | } | ||
diff --git a/src/firejail/fs_hostname.c b/src/firejail/fs_hostname.c index aa391c0cb..04197eb8f 100644 --- a/src/firejail/fs_hostname.c +++ b/src/firejail/fs_hostname.c | |||
@@ -27,7 +27,6 @@ | |||
27 | 27 | ||
28 | void fs_hostname(const char *hostname) { | 28 | void fs_hostname(const char *hostname) { |
29 | struct stat s; | 29 | struct stat s; |
30 | fs_build_mnt_dir(); | ||
31 | 30 | ||
32 | // create a new /etc/hostname | 31 | // create a new /etc/hostname |
33 | if (stat("/etc/hostname", &s) == 0) { | 32 | if (stat("/etc/hostname", &s) == 0) { |
@@ -40,14 +39,10 @@ void fs_hostname(const char *hostname) { | |||
40 | exit(1); | 39 | exit(1); |
41 | } | 40 | } |
42 | fprintf(fp, "%s\n", hostname); | 41 | fprintf(fp, "%s\n", hostname); |
43 | fclose(fp); | ||
44 | |||
45 | // mode and owner | 42 | // mode and owner |
46 | if (chown(RUN_HOSTNAME_FILE, 0, 0) < 0) | 43 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); |
47 | errExit("chown"); | 44 | fclose(fp); |
48 | if (chmod(RUN_HOSTNAME_FILE, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0) | 45 | |
49 | errExit("chmod"); | ||
50 | |||
51 | // bind-mount the file on top of /etc/hostname | 46 | // bind-mount the file on top of /etc/hostname |
52 | if (mount(RUN_HOSTNAME_FILE, "/etc/hostname", NULL, MS_BIND|MS_REC, NULL) < 0) | 47 | if (mount(RUN_HOSTNAME_FILE, "/etc/hostname", NULL, MS_BIND|MS_REC, NULL) < 0) |
53 | errExit("mount bind /etc/hostname"); | 48 | errExit("mount bind /etc/hostname"); |
@@ -88,13 +83,9 @@ void fs_hostname(const char *hostname) { | |||
88 | fprintf(fp2, "%s\n", buf); | 83 | fprintf(fp2, "%s\n", buf); |
89 | } | 84 | } |
90 | fclose(fp1); | 85 | fclose(fp1); |
91 | fclose(fp2); | ||
92 | |||
93 | // mode and owner | 86 | // mode and owner |
94 | if (chown(RUN_HOSTS_FILE, 0, 0) < 0) | 87 | SET_PERMS_STREAM(fp2, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); |
95 | errExit("chown"); | 88 | fclose(fp2); |
96 | if (chmod(RUN_HOSTS_FILE, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0) | ||
97 | errExit("chmod"); | ||
98 | 89 | ||
99 | // bind-mount the file on top of /etc/hostname | 90 | // bind-mount the file on top of /etc/hostname |
100 | if (mount(RUN_HOSTS_FILE, "/etc/hosts", NULL, MS_BIND|MS_REC, NULL) < 0) | 91 | if (mount(RUN_HOSTS_FILE, "/etc/hosts", NULL, MS_BIND|MS_REC, NULL) < 0) |
@@ -108,7 +99,6 @@ void fs_resolvconf(void) { | |||
108 | return; | 99 | return; |
109 | 100 | ||
110 | struct stat s; | 101 | struct stat s; |
111 | fs_build_mnt_dir(); | ||
112 | 102 | ||
113 | // create a new /etc/hostname | 103 | // create a new /etc/hostname |
114 | if (stat("/etc/resolv.conf", &s) == 0) { | 104 | if (stat("/etc/resolv.conf", &s) == 0) { |
@@ -126,13 +116,11 @@ void fs_resolvconf(void) { | |||
126 | fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); | 116 | fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); |
127 | if (cfg.dns3) | 117 | if (cfg.dns3) |
128 | fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); | 118 | fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); |
129 | fclose(fp); | 119 | |
130 | |||
131 | // mode and owner | 120 | // mode and owner |
132 | if (chown(RUN_RESOLVCONF_FILE, 0, 0) < 0) | 121 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); |
133 | errExit("chown"); | 122 | |
134 | if (chmod(RUN_RESOLVCONF_FILE, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0) | 123 | fclose(fp); |
135 | errExit("chmod"); | ||
136 | 124 | ||
137 | // bind-mount the file on top of /etc/hostname | 125 | // bind-mount the file on top of /etc/hostname |
138 | if (mount(RUN_RESOLVCONF_FILE, "/etc/resolv.conf", NULL, MS_BIND|MS_REC, NULL) < 0) | 126 | if (mount(RUN_RESOLVCONF_FILE, "/etc/resolv.conf", NULL, MS_BIND|MS_REC, NULL) < 0) |
diff --git a/src/firejail/fs_logger.c b/src/firejail/fs_logger.c index 30b0fe438..052a41457 100644 --- a/src/firejail/fs_logger.c +++ b/src/firejail/fs_logger.c | |||
@@ -97,11 +97,7 @@ void fs_logger_print(void) { | |||
97 | perror("fopen"); | 97 | perror("fopen"); |
98 | return; | 98 | return; |
99 | } | 99 | } |
100 | 100 | SET_PERMS_STREAM_NOERR(fp, getuid(), getgid(), 0644); | |
101 | int rv = chown(RUN_FSLOGGER_FILE, getuid(), getgid()); | ||
102 | (void) rv; // best effort! | ||
103 | rv = chmod(RUN_FSLOGGER_FILE, 0644); | ||
104 | (void) rv; // best effort! | ||
105 | 101 | ||
106 | FsMsg *ptr = head; | 102 | FsMsg *ptr = head; |
107 | while (ptr) { | 103 | while (ptr) { |
@@ -121,22 +117,6 @@ void fs_logger_change_owner(void) { | |||
121 | errExit("chown"); | 117 | errExit("chown"); |
122 | } | 118 | } |
123 | 119 | ||
124 | void fs_logger_print_log_name(const char *name) { | ||
125 | EUID_ASSERT(); | ||
126 | |||
127 | if (!name || strlen(name) == 0) { | ||
128 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
129 | exit(1); | ||
130 | } | ||
131 | pid_t pid; | ||
132 | if (name2pid(name, &pid)) { | ||
133 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
134 | exit(1); | ||
135 | } | ||
136 | |||
137 | fs_logger_print_log(pid); | ||
138 | } | ||
139 | |||
140 | void fs_logger_print_log(pid_t pid) { | 120 | void fs_logger_print_log(pid_t pid) { |
141 | EUID_ASSERT(); | 121 | EUID_ASSERT(); |
142 | 122 | ||
diff --git a/src/firejail/fs_mkdir.c b/src/firejail/fs_mkdir.c index 398c534bf..6bcb3f33e 100644 --- a/src/firejail/fs_mkdir.c +++ b/src/firejail/fs_mkdir.c | |||
@@ -22,8 +22,38 @@ | |||
22 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
23 | #include <unistd.h> | 23 | #include <unistd.h> |
24 | #include <grp.h> | 24 | #include <grp.h> |
25 | #include <sys/wait.h> | 25 | #include <sys/wait.h> |
26 | 26 | #include <string.h> | |
27 | |||
28 | static void mkdir_recursive(char *path) { | ||
29 | char *subdir = NULL; | ||
30 | struct stat s; | ||
31 | |||
32 | if (chdir("/")) { | ||
33 | fprintf(stderr, "Error: can't chdir to /"); | ||
34 | return; | ||
35 | } | ||
36 | |||
37 | subdir = strtok(path, "/"); | ||
38 | while(subdir) { | ||
39 | if (stat(subdir, &s) == -1) { | ||
40 | if (mkdir(subdir, 0700) == -1) { | ||
41 | fprintf(stderr, "Warning: cannot create %s directory\n", subdir); | ||
42 | return; | ||
43 | } | ||
44 | } else if (!S_ISDIR(s.st_mode)) { | ||
45 | fprintf(stderr, "Warning: '%s' exists, but is no directory\n", subdir); | ||
46 | return; | ||
47 | } | ||
48 | if (chdir(subdir)) { | ||
49 | fprintf(stderr, "Error: can't chdir to %s", subdir); | ||
50 | return; | ||
51 | } | ||
52 | |||
53 | subdir = strtok(NULL, "/"); | ||
54 | } | ||
55 | } | ||
56 | |||
27 | void fs_mkdir(const char *name) { | 57 | void fs_mkdir(const char *name) { |
28 | EUID_ASSERT(); | 58 | EUID_ASSERT(); |
29 | 59 | ||
@@ -42,9 +72,71 @@ void fs_mkdir(const char *name) { | |||
42 | } | 72 | } |
43 | 73 | ||
44 | // create directory | 74 | // create directory |
45 | if (mkdir(expanded, 0700) == -1) | 75 | pid_t child = fork(); |
46 | fprintf(stderr, "Warning: cannot create %s directory\n", expanded); | 76 | if (child < 0) |
77 | errExit("fork"); | ||
78 | if (child == 0) { | ||
79 | // drop privileges | ||
80 | drop_privs(0); | ||
81 | |||
82 | // create directory | ||
83 | mkdir_recursive(expanded); | ||
84 | #ifdef HAVE_GCOV | ||
85 | __gcov_flush(); | ||
86 | #endif | ||
87 | _exit(0); | ||
88 | } | ||
89 | // wait for the child to finish | ||
90 | waitpid(child, NULL, 0); | ||
47 | 91 | ||
48 | doexit: | 92 | doexit: |
49 | free(expanded); | 93 | free(expanded); |
50 | } | 94 | } |
95 | |||
96 | void fs_mkfile(const char *name) { | ||
97 | EUID_ASSERT(); | ||
98 | |||
99 | // check file name | ||
100 | invalid_filename(name); | ||
101 | char *expanded = expand_home(name, cfg.homedir); | ||
102 | if (strncmp(expanded, cfg.homedir, strlen(cfg.homedir)) != 0) { | ||
103 | fprintf(stderr, "Error: only files in user home are supported by mkfile\n"); | ||
104 | exit(1); | ||
105 | } | ||
106 | |||
107 | struct stat s; | ||
108 | if (stat(expanded, &s) == 0) { | ||
109 | // file exists, do nothing | ||
110 | goto doexit; | ||
111 | } | ||
112 | |||
113 | // create file | ||
114 | pid_t child = fork(); | ||
115 | if (child < 0) | ||
116 | errExit("fork"); | ||
117 | if (child == 0) { | ||
118 | // drop privileges | ||
119 | drop_privs(0); | ||
120 | |||
121 | FILE *fp = fopen(expanded, "w"); | ||
122 | if (!fp) | ||
123 | fprintf(stderr, "Warning: cannot create %s file\n", expanded); | ||
124 | else { | ||
125 | int fd = fileno(fp); | ||
126 | if (fd == -1) | ||
127 | errExit("fileno"); | ||
128 | int rv = fchmod(fd, 0600); | ||
129 | (void) rv; | ||
130 | fclose(fp); | ||
131 | } | ||
132 | #ifdef HAVE_GCOV | ||
133 | __gcov_flush(); | ||
134 | #endif | ||
135 | _exit(0); | ||
136 | } | ||
137 | // wait for the child to finish | ||
138 | waitpid(child, NULL, 0); | ||
139 | |||
140 | doexit: | ||
141 | free(expanded); | ||
142 | } | ||
diff --git a/src/firejail/fs_trace.c b/src/firejail/fs_trace.c index f6ca28227..719b55048 100644 --- a/src/firejail/fs_trace.c +++ b/src/firejail/fs_trace.c | |||
@@ -37,19 +37,13 @@ void fs_trace_preload(void) { | |||
37 | FILE *fp = fopen("/etc/ld.so.preload", "w"); | 37 | FILE *fp = fopen("/etc/ld.so.preload", "w"); |
38 | if (!fp) | 38 | if (!fp) |
39 | errExit("fopen"); | 39 | errExit("fopen"); |
40 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); | ||
40 | fclose(fp); | 41 | fclose(fp); |
41 | if (chown("/etc/ld.so.preload", 0, 0) < 0) | ||
42 | errExit("chown"); | ||
43 | if (chmod("/etc/ld.so.preload", S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0) | ||
44 | errExit("chmod"); | ||
45 | fs_logger("touch /etc/ld.so.preload"); | 42 | fs_logger("touch /etc/ld.so.preload"); |
46 | } | 43 | } |
47 | } | 44 | } |
48 | 45 | ||
49 | void fs_trace(void) { | 46 | void fs_trace(void) { |
50 | // create /tmp/firejail/mnt directory | ||
51 | fs_build_mnt_dir(); | ||
52 | |||
53 | // create the new ld.so.preload file and mount-bind it | 47 | // create the new ld.so.preload file and mount-bind it |
54 | if (arg_debug) | 48 | if (arg_debug) |
55 | printf("Create the new ld.so.preload file\n"); | 49 | printf("Create the new ld.so.preload file\n"); |
@@ -57,21 +51,20 @@ void fs_trace(void) { | |||
57 | FILE *fp = fopen(RUN_LDPRELOAD_FILE, "w"); | 51 | FILE *fp = fopen(RUN_LDPRELOAD_FILE, "w"); |
58 | if (!fp) | 52 | if (!fp) |
59 | errExit("fopen"); | 53 | errExit("fopen"); |
60 | if (arg_trace) | 54 | if (arg_trace) { |
61 | fprintf(fp, "%s/firejail/libtrace.so\n", LIBDIR); | 55 | fprintf(fp, "%s/firejail/libtrace.so\n", LIBDIR); |
56 | } | ||
62 | else if (arg_tracelog) { | 57 | else if (arg_tracelog) { |
63 | fprintf(fp, "%s/firejail/libtracelog.so\n", LIBDIR); | 58 | fprintf(fp, "%s/firejail/libtracelog.so\n", LIBDIR); |
64 | if (!arg_quiet) | 59 | if (!arg_quiet) |
65 | printf("Blacklist violations are logged to syslog\n"); | 60 | printf("Blacklist violations are logged to syslog\n"); |
66 | } | 61 | } |
67 | else | 62 | |
68 | assert(0); | 63 | if (mask_x11_abstract_socket) |
69 | 64 | fprintf(fp, "%s/firejail/libconnect.so\n", LIBDIR); | |
65 | |||
66 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); | ||
70 | fclose(fp); | 67 | fclose(fp); |
71 | if (chown(RUN_LDPRELOAD_FILE, 0, 0) < 0) | ||
72 | errExit("chown"); | ||
73 | if (chmod(RUN_LDPRELOAD_FILE, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0) | ||
74 | errExit("chmod"); | ||
75 | 68 | ||
76 | // mount the new preload file | 69 | // mount the new preload file |
77 | if (arg_debug) | 70 | if (arg_debug) |
@@ -81,5 +74,3 @@ void fs_trace(void) { | |||
81 | fs_logger("create /etc/ld.so.preload"); | 74 | fs_logger("create /etc/ld.so.preload"); |
82 | } | 75 | } |
83 | 76 | ||
84 | |||
85 | \ No newline at end of file | ||
diff --git a/src/firejail/fs_var.c b/src/firejail/fs_var.c index f904fa5d9..ca50685ad 100644 --- a/src/firejail/fs_var.c +++ b/src/firejail/fs_var.c | |||
@@ -98,10 +98,7 @@ static void build_dirs(void) { | |||
98 | // create directories under /var/log | 98 | // create directories under /var/log |
99 | DirData *ptr = dirlist; | 99 | DirData *ptr = dirlist; |
100 | while (ptr) { | 100 | while (ptr) { |
101 | if (mkdir(ptr->name, ptr->st_mode)) | 101 | mkdir_attr(ptr->name, ptr->st_mode, ptr->st_uid, ptr->st_gid); |
102 | errExit("mkdir"); | ||
103 | if (chown(ptr->name, ptr->st_uid, ptr->st_gid)) | ||
104 | errExit("chown"); | ||
105 | fs_logger2("mkdir", ptr->name); | 102 | fs_logger2("mkdir", ptr->name); |
106 | ptr = ptr->next; | 103 | ptr = ptr->next; |
107 | } | 104 | } |
@@ -121,7 +118,7 @@ void fs_var_log(void) { | |||
121 | // mount a tmpfs on top of /var/log | 118 | // mount a tmpfs on top of /var/log |
122 | if (arg_debug) | 119 | if (arg_debug) |
123 | printf("Mounting tmpfs on /var/log\n"); | 120 | printf("Mounting tmpfs on /var/log\n"); |
124 | if (mount("tmpfs", "/var/log", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 121 | if (mount("tmpfs", "/var/log", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
125 | errExit("mounting /var/log"); | 122 | errExit("mounting /var/log"); |
126 | fs_logger("tmpfs /var/log"); | 123 | fs_logger("tmpfs /var/log"); |
127 | 124 | ||
@@ -131,22 +128,16 @@ void fs_var_log(void) { | |||
131 | // create an empty /var/log/wtmp file | 128 | // create an empty /var/log/wtmp file |
132 | /* coverity[toctou] */ | 129 | /* coverity[toctou] */ |
133 | FILE *fp = fopen("/var/log/wtmp", "w"); | 130 | FILE *fp = fopen("/var/log/wtmp", "w"); |
131 | SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH); | ||
134 | if (fp) | 132 | if (fp) |
135 | fclose(fp); | 133 | fclose(fp); |
136 | if (chown("/var/log/wtmp", 0, wtmp_group) < 0) | ||
137 | errExit("chown"); | ||
138 | if (chmod("/var/log/wtmp", S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH ) < 0) | ||
139 | errExit("chmod"); | ||
140 | fs_logger("touch /var/log/wtmp"); | 134 | fs_logger("touch /var/log/wtmp"); |
141 | 135 | ||
142 | // create an empty /var/log/btmp file | 136 | // create an empty /var/log/btmp file |
143 | fp = fopen("/var/log/btmp", "w"); | 137 | fp = fopen("/var/log/btmp", "w"); |
138 | SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP); | ||
144 | if (fp) | 139 | if (fp) |
145 | fclose(fp); | 140 | fclose(fp); |
146 | if (chown("/var/log/btmp", 0, wtmp_group) < 0) | ||
147 | errExit("chown"); | ||
148 | if (chmod("/var/log/btmp", S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP) < 0) | ||
149 | errExit("chmod"); | ||
150 | fs_logger("touch /var/log/btmp"); | 141 | fs_logger("touch /var/log/btmp"); |
151 | } | 142 | } |
152 | else | 143 | else |
@@ -160,7 +151,7 @@ void fs_var_lib(void) { | |||
160 | if (stat("/var/lib/dhcp", &s) == 0) { | 151 | if (stat("/var/lib/dhcp", &s) == 0) { |
161 | if (arg_debug) | 152 | if (arg_debug) |
162 | printf("Mounting tmpfs on /var/lib/dhcp\n"); | 153 | printf("Mounting tmpfs on /var/lib/dhcp\n"); |
163 | if (mount("tmpfs", "/var/lib/dhcp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 154 | if (mount("tmpfs", "/var/lib/dhcp", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
164 | errExit("mounting /var/lib/dhcp"); | 155 | errExit("mounting /var/lib/dhcp"); |
165 | fs_logger("tmpfs /var/lib/dhcp"); | 156 | fs_logger("tmpfs /var/lib/dhcp"); |
166 | 157 | ||
@@ -169,11 +160,8 @@ void fs_var_lib(void) { | |||
169 | 160 | ||
170 | if (fp) { | 161 | if (fp) { |
171 | fprintf(fp, "\n"); | 162 | fprintf(fp, "\n"); |
163 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); | ||
172 | fclose(fp); | 164 | fclose(fp); |
173 | if (chown("/var/lib/dhcp/dhcpd.leases", 0, 0) == -1) | ||
174 | errExit("chown"); | ||
175 | if (chmod("/var/lib/dhcp/dhcpd.leases", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) | ||
176 | errExit("chmod"); | ||
177 | fs_logger("touch /var/lib/dhcp/dhcpd.leases"); | 165 | fs_logger("touch /var/lib/dhcp/dhcpd.leases"); |
178 | } | 166 | } |
179 | } | 167 | } |
@@ -182,7 +170,7 @@ void fs_var_lib(void) { | |||
182 | if (stat("/var/lib/nginx", &s) == 0) { | 170 | if (stat("/var/lib/nginx", &s) == 0) { |
183 | if (arg_debug) | 171 | if (arg_debug) |
184 | printf("Mounting tmpfs on /var/lib/nginx\n"); | 172 | printf("Mounting tmpfs on /var/lib/nginx\n"); |
185 | if (mount("tmpfs", "/var/lib/nginx", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 173 | if (mount("tmpfs", "/var/lib/nginx", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
186 | errExit("mounting /var/lib/nginx"); | 174 | errExit("mounting /var/lib/nginx"); |
187 | fs_logger("tmpfs /var/lib/nginx"); | 175 | fs_logger("tmpfs /var/lib/nginx"); |
188 | } | 176 | } |
@@ -191,7 +179,7 @@ void fs_var_lib(void) { | |||
191 | if (stat("/var/lib/snmp", &s) == 0) { | 179 | if (stat("/var/lib/snmp", &s) == 0) { |
192 | if (arg_debug) | 180 | if (arg_debug) |
193 | printf("Mounting tmpfs on /var/lib/snmp\n"); | 181 | printf("Mounting tmpfs on /var/lib/snmp\n"); |
194 | if (mount("tmpfs", "/var/lib/snmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 182 | if (mount("tmpfs", "/var/lib/snmp", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
195 | errExit("mounting /var/lib/snmp"); | 183 | errExit("mounting /var/lib/snmp"); |
196 | fs_logger("tmpfs /var/lib/snmp"); | 184 | fs_logger("tmpfs /var/lib/snmp"); |
197 | } | 185 | } |
@@ -200,7 +188,7 @@ void fs_var_lib(void) { | |||
200 | if (stat("/var/lib/sudo", &s) == 0) { | 188 | if (stat("/var/lib/sudo", &s) == 0) { |
201 | if (arg_debug) | 189 | if (arg_debug) |
202 | printf("Mounting tmpfs on /var/lib/sudo\n"); | 190 | printf("Mounting tmpfs on /var/lib/sudo\n"); |
203 | if (mount("tmpfs", "/var/lib/sudo", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 191 | if (mount("tmpfs", "/var/lib/sudo", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
204 | errExit("mounting /var/lib/sudo"); | 192 | errExit("mounting /var/lib/sudo"); |
205 | fs_logger("tmpfs /var/lib/sudo"); | 193 | fs_logger("tmpfs /var/lib/sudo"); |
206 | } | 194 | } |
@@ -212,7 +200,7 @@ void fs_var_cache(void) { | |||
212 | if (stat("/var/cache/apache2", &s) == 0) { | 200 | if (stat("/var/cache/apache2", &s) == 0) { |
213 | if (arg_debug) | 201 | if (arg_debug) |
214 | printf("Mounting tmpfs on /var/cache/apache2\n"); | 202 | printf("Mounting tmpfs on /var/cache/apache2\n"); |
215 | if (mount("tmpfs", "/var/cache/apache2", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 203 | if (mount("tmpfs", "/var/cache/apache2", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
216 | errExit("mounting /var/cache/apache2"); | 204 | errExit("mounting /var/cache/apache2"); |
217 | fs_logger("tmpfs /var/cache/apache2"); | 205 | fs_logger("tmpfs /var/cache/apache2"); |
218 | } | 206 | } |
@@ -220,7 +208,7 @@ void fs_var_cache(void) { | |||
220 | if (stat("/var/cache/lighttpd", &s) == 0) { | 208 | if (stat("/var/cache/lighttpd", &s) == 0) { |
221 | if (arg_debug) | 209 | if (arg_debug) |
222 | printf("Mounting tmpfs on /var/cache/lighttpd\n"); | 210 | printf("Mounting tmpfs on /var/cache/lighttpd\n"); |
223 | if (mount("tmpfs", "/var/cache/lighttpd", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 211 | if (mount("tmpfs", "/var/cache/lighttpd", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
224 | errExit("mounting /var/cache/lighttpd"); | 212 | errExit("mounting /var/cache/lighttpd"); |
225 | fs_logger("tmpfs /var/cache/lighttpd"); | 213 | fs_logger("tmpfs /var/cache/lighttpd"); |
226 | 214 | ||
@@ -232,18 +220,10 @@ void fs_var_cache(void) { | |||
232 | gid = p->pw_gid; | 220 | gid = p->pw_gid; |
233 | } | 221 | } |
234 | 222 | ||
235 | int rv = mkdir("/var/cache/lighttpd/compress", 0755); | 223 | mkdir_attr("/var/cache/lighttpd/compress", 0755, uid, gid); |
236 | if (rv == -1) | ||
237 | errExit("mkdir"); | ||
238 | if (chown("/var/cache/lighttpd/compress", uid, gid) < 0) | ||
239 | errExit("chown"); | ||
240 | fs_logger("mkdir /var/cache/lighttpd/compress"); | 224 | fs_logger("mkdir /var/cache/lighttpd/compress"); |
241 | 225 | ||
242 | rv = mkdir("/var/cache/lighttpd/uploads", 0755); | 226 | mkdir_attr("/var/cache/lighttpd/uploads", 0755, uid, gid); |
243 | if (rv == -1) | ||
244 | errExit("mkdir"); | ||
245 | if (chown("/var/cache/lighttpd/uploads", uid, gid) < 0) | ||
246 | errExit("chown"); | ||
247 | fs_logger("/var/cache/lighttpd/uploads"); | 227 | fs_logger("/var/cache/lighttpd/uploads"); |
248 | } | 228 | } |
249 | } | 229 | } |
@@ -268,7 +248,7 @@ void fs_var_lock(void) { | |||
268 | if (is_dir("/var/lock")) { | 248 | if (is_dir("/var/lock")) { |
269 | if (arg_debug) | 249 | if (arg_debug) |
270 | printf("Mounting tmpfs on /var/lock\n"); | 250 | printf("Mounting tmpfs on /var/lock\n"); |
271 | if (mount("tmpfs", "/var/lock", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) | 251 | if (mount("tmpfs", "/var/lock", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) |
272 | errExit("mounting /lock"); | 252 | errExit("mounting /lock"); |
273 | fs_logger("tmpfs /var/lock"); | 253 | fs_logger("tmpfs /var/lock"); |
274 | } | 254 | } |
@@ -277,16 +257,11 @@ void fs_var_lock(void) { | |||
277 | if (lnk) { | 257 | if (lnk) { |
278 | if (!is_dir(lnk)) { | 258 | if (!is_dir(lnk)) { |
279 | // create directory | 259 | // create directory |
280 | if (mkdir(lnk, S_IRWXU|S_IRWXG|S_IRWXO)) | 260 | mkdir_attr(lnk, S_IRWXU|S_IRWXG|S_IRWXO, 0, 0); |
281 | errExit("mkdir"); | ||
282 | if (chown(lnk, 0, 0)) | ||
283 | errExit("chown"); | ||
284 | if (chmod(lnk, S_IRWXU|S_IRWXG|S_IRWXO)) | ||
285 | errExit("chmod"); | ||
286 | } | 261 | } |
287 | if (arg_debug) | 262 | if (arg_debug) |
288 | printf("Mounting tmpfs on %s on behalf of /var/lock\n", lnk); | 263 | printf("Mounting tmpfs on %s on behalf of /var/lock\n", lnk); |
289 | if (mount("tmpfs", lnk, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) | 264 | if (mount("tmpfs", lnk, "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) |
290 | errExit("mounting /var/lock"); | 265 | errExit("mounting /var/lock"); |
291 | free(lnk); | 266 | free(lnk); |
292 | fs_logger("tmpfs /var/lock"); | 267 | fs_logger("tmpfs /var/lock"); |
@@ -304,7 +279,7 @@ void fs_var_tmp(void) { | |||
304 | if (!is_link("/var/tmp")) { | 279 | if (!is_link("/var/tmp")) { |
305 | if (arg_debug) | 280 | if (arg_debug) |
306 | printf("Mounting tmpfs on /var/tmp\n"); | 281 | printf("Mounting tmpfs on /var/tmp\n"); |
307 | if (mount("tmpfs", "/var/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) | 282 | if (mount("tmpfs", "/var/tmp", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) |
308 | errExit("mounting /var/tmp"); | 283 | errExit("mounting /var/tmp"); |
309 | fs_logger("tmpfs /var/tmp"); | 284 | fs_logger("tmpfs /var/tmp"); |
310 | } | 285 | } |
@@ -327,9 +302,6 @@ void fs_var_utmp(void) { | |||
327 | return; | 302 | return; |
328 | } | 303 | } |
329 | 304 | ||
330 | // create /tmp/firejail/mnt directory | ||
331 | fs_build_mnt_dir(); | ||
332 | |||
333 | // create a new utmp file | 305 | // create a new utmp file |
334 | if (arg_debug) | 306 | if (arg_debug) |
335 | printf("Create the new utmp file\n"); | 307 | printf("Create the new utmp file\n"); |
@@ -353,16 +325,13 @@ void fs_var_utmp(void) { | |||
353 | 325 | ||
354 | // save new utmp file | 326 | // save new utmp file |
355 | fwrite(&u_boot, sizeof(u_boot), 1, fp); | 327 | fwrite(&u_boot, sizeof(u_boot), 1, fp); |
328 | SET_PERMS_STREAM(fp, 0, utmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH); | ||
356 | fclose(fp); | 329 | fclose(fp); |
357 | if (chown(RUN_UTMP_FILE, 0, utmp_group) < 0) | ||
358 | errExit("chown"); | ||
359 | if (chmod(RUN_UTMP_FILE, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH ) < 0) | ||
360 | errExit("chmod"); | ||
361 | 330 | ||
362 | // mount the new utmp file | 331 | // mount the new utmp file |
363 | if (arg_debug) | 332 | if (arg_debug) |
364 | printf("Mount the new utmp file\n"); | 333 | printf("Mount the new utmp file\n"); |
365 | if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_REC, NULL) < 0) | 334 | if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_NOSUID|MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) |
366 | errExit("mount bind utmp"); | 335 | errExit("mount bind utmp"); |
367 | fs_logger("create /var/run/utmp"); | 336 | fs_logger("create /var/run/utmp"); |
368 | } | 337 | } |
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index 617e61dcd..564dc8290 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -157,10 +157,8 @@ static int mkpath(const char* path, mode_t mode) { | |||
157 | } | 157 | } |
158 | } | 158 | } |
159 | else { | 159 | else { |
160 | if (chmod(file_path, mode) == -1) | 160 | if (set_perms(file_path, uid, gid, mode)) |
161 | errExit("chmod"); | 161 | errExit("set_perms"); |
162 | if (chown(file_path, uid, gid) == -1) | ||
163 | errExit("chown"); | ||
164 | done = 1; | 162 | done = 1; |
165 | } | 163 | } |
166 | 164 | ||
@@ -181,11 +179,15 @@ static void whitelist_path(ProfileEntry *entry) { | |||
181 | char *wfile = NULL; | 179 | char *wfile = NULL; |
182 | 180 | ||
183 | if (entry->home_dir) { | 181 | if (entry->home_dir) { |
184 | fname = path + strlen(cfg.homedir); | 182 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) == 0) { |
185 | if (*fname == '\0') { | 183 | fname = path + strlen(cfg.homedir); |
186 | fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path); | 184 | if (*fname == '\0') { |
187 | exit(1); | 185 | fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path); |
186 | exit(1); | ||
187 | } | ||
188 | } | 188 | } |
189 | else | ||
190 | fname = path; | ||
189 | 191 | ||
190 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1) | 192 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1) |
191 | errExit("asprintf"); | 193 | errExit("asprintf"); |
@@ -210,6 +212,16 @@ static void whitelist_path(ProfileEntry *entry) { | |||
210 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MEDIA_DIR, fname) == -1) | 212 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MEDIA_DIR, fname) == -1) |
211 | errExit("asprintf"); | 213 | errExit("asprintf"); |
212 | } | 214 | } |
215 | else if (entry->mnt_dir) { | ||
216 | fname = path + 4; // strlen("/mnt") | ||
217 | if (*fname == '\0') { | ||
218 | fprintf(stderr, "Error: file %s is not in /mnt directory, exiting...\n", path); | ||
219 | exit(1); | ||
220 | } | ||
221 | |||
222 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MNT_DIR, fname) == -1) | ||
223 | errExit("asprintf"); | ||
224 | } | ||
213 | else if (entry->var_dir) { | 225 | else if (entry->var_dir) { |
214 | fname = path + 4; // strlen("/var") | 226 | fname = path + 4; // strlen("/var") |
215 | if (*fname == '\0') { | 227 | if (*fname == '\0') { |
@@ -240,7 +252,16 @@ static void whitelist_path(ProfileEntry *entry) { | |||
240 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_OPT_DIR, fname) == -1) | 252 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_OPT_DIR, fname) == -1) |
241 | errExit("asprintf"); | 253 | errExit("asprintf"); |
242 | } | 254 | } |
255 | else if (entry->srv_dir) { | ||
256 | fname = path + 4; // strlen("/srv") | ||
257 | if (*fname == '\0') { | ||
258 | fprintf(stderr, "Error: file %s is not in /srv directory, exiting...\n", path); | ||
259 | exit(1); | ||
260 | } | ||
243 | 261 | ||
262 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SRV_DIR, fname) == -1) | ||
263 | errExit("asprintf"); | ||
264 | } | ||
244 | // check if the file exists | 265 | // check if the file exists |
245 | struct stat s; | 266 | struct stat s; |
246 | if (wfile && stat(wfile, &s) == 0) { | 267 | if (wfile && stat(wfile, &s) == 0) { |
@@ -248,9 +269,6 @@ static void whitelist_path(ProfileEntry *entry) { | |||
248 | printf("Whitelisting %s\n", path); | 269 | printf("Whitelisting %s\n", path); |
249 | } | 270 | } |
250 | else { | 271 | else { |
251 | if (arg_debug || arg_debug_whitelists) { | ||
252 | fprintf(stderr, "Warning (whitelisting): %s is an invalid file, skipping...\n", path); | ||
253 | } | ||
254 | return; | 272 | return; |
255 | } | 273 | } |
256 | 274 | ||
@@ -267,21 +285,21 @@ static void whitelist_path(ProfileEntry *entry) { | |||
267 | 285 | ||
268 | // process regular file | 286 | // process regular file |
269 | else { | 287 | else { |
270 | // create an empty file | 288 | if (access(path, R_OK)) { |
271 | FILE *fp = fopen(path, "w"); | 289 | // create an empty file |
272 | if (!fp) { | 290 | FILE *fp = fopen(path, "w"); |
273 | fprintf(stderr, "Error: cannot create empty file in home directory\n"); | 291 | if (!fp) { |
274 | exit(1); | 292 | fprintf(stderr, "Error: cannot create empty file in home directory\n"); |
293 | exit(1); | ||
294 | } | ||
295 | // set file properties | ||
296 | SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); | ||
297 | fclose(fp); | ||
275 | } | 298 | } |
276 | fclose(fp); | 299 | else |
300 | return; // the file is already present | ||
277 | } | 301 | } |
278 | 302 | ||
279 | // set file properties | ||
280 | if (chown(path, s.st_uid, s.st_gid) < 0) | ||
281 | errExit("chown"); | ||
282 | if (chmod(path, s.st_mode) < 0) | ||
283 | errExit("chmod"); | ||
284 | |||
285 | // mount | 303 | // mount |
286 | if (mount(wfile, path, NULL, MS_BIND|MS_REC, NULL) < 0) | 304 | if (mount(wfile, path, NULL, MS_BIND|MS_REC, NULL) < 0) |
287 | errExit("mount bind"); | 305 | errExit("mount bind"); |
@@ -302,10 +320,11 @@ void fs_whitelist(void) { | |||
302 | int home_dir = 0; // /home/user directory flag | 320 | int home_dir = 0; // /home/user directory flag |
303 | int tmp_dir = 0; // /tmp directory flag | 321 | int tmp_dir = 0; // /tmp directory flag |
304 | int media_dir = 0; // /media directory flag | 322 | int media_dir = 0; // /media directory flag |
323 | int mnt_dir = 0; // /mnt directory flag | ||
305 | int var_dir = 0; // /var directory flag | 324 | int var_dir = 0; // /var directory flag |
306 | int dev_dir = 0; // /dev directory flag | 325 | int dev_dir = 0; // /dev directory flag |
307 | int opt_dir = 0; // /opt directory flag | 326 | int opt_dir = 0; // /opt directory flag |
308 | 327 | int srv_dir = 0; // /srv directory flag | |
309 | // verify whitelist files, extract symbolic links, etc. | 328 | // verify whitelist files, extract symbolic links, etc. |
310 | while (entry) { | 329 | while (entry) { |
311 | // handle only whitelist commands | 330 | // handle only whitelist commands |
@@ -367,13 +386,17 @@ void fs_whitelist(void) { | |||
367 | tmp_dir = 1; | 386 | tmp_dir = 1; |
368 | else if (strncmp(new_name, "/media/", 7) == 0) | 387 | else if (strncmp(new_name, "/media/", 7) == 0) |
369 | media_dir = 1; | 388 | media_dir = 1; |
389 | else if (strncmp(new_name, "/mnt/", 5) == 0) | ||
390 | mnt_dir = 1; | ||
370 | else if (strncmp(new_name, "/var/", 5) == 0) | 391 | else if (strncmp(new_name, "/var/", 5) == 0) |
371 | var_dir = 1; | 392 | var_dir = 1; |
372 | else if (strncmp(new_name, "/dev/", 5) == 0) | 393 | else if (strncmp(new_name, "/dev/", 5) == 0) |
373 | dev_dir = 1; | 394 | dev_dir = 1; |
374 | else if (strncmp(new_name, "/opt/", 5) == 0) | 395 | else if (strncmp(new_name, "/opt/", 5) == 0) |
375 | opt_dir = 1; | 396 | opt_dir = 1; |
376 | 397 | else if (strncmp(new_name, "/srv/", 5) == 0) | |
398 | opt_dir = 1; | ||
399 | |||
377 | continue; | 400 | continue; |
378 | } | 401 | } |
379 | 402 | ||
@@ -390,12 +413,16 @@ void fs_whitelist(void) { | |||
390 | 413 | ||
391 | entry->home_dir = 1; | 414 | entry->home_dir = 1; |
392 | home_dir = 1; | 415 | home_dir = 1; |
416 | if (arg_debug || arg_debug_whitelists) | ||
417 | fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n", | ||
418 | __LINE__, fname, cfg.homedir); | ||
419 | |||
393 | // both path and absolute path are under /home | 420 | // both path and absolute path are under /home |
394 | if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) != 0) { | 421 | if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) != 0) { |
395 | if (arg_debug) | 422 | // check if the file is owned by the user |
396 | fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n", | 423 | struct stat s; |
397 | __LINE__, fname, cfg.homedir); | 424 | if (stat(fname, &s) == 0 && s.st_uid != getuid()) |
398 | goto errexit; | 425 | goto errexit; |
399 | } | 426 | } |
400 | } | 427 | } |
401 | else if (strncmp(new_name, "/tmp/", 5) == 0) { | 428 | else if (strncmp(new_name, "/tmp/", 5) == 0) { |
@@ -418,6 +445,16 @@ void fs_whitelist(void) { | |||
418 | goto errexit; | 445 | goto errexit; |
419 | } | 446 | } |
420 | } | 447 | } |
448 | else if (strncmp(new_name, "/mnt/", 5) == 0) { | ||
449 | entry->mnt_dir = 1; | ||
450 | mnt_dir = 1; | ||
451 | // both path and absolute path are under /mnt | ||
452 | if (strncmp(fname, "/mnt/", 5) != 0) { | ||
453 | if (arg_debug) | ||
454 | fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); | ||
455 | goto errexit; | ||
456 | } | ||
457 | } | ||
421 | else if (strncmp(new_name, "/var/", 5) == 0) { | 458 | else if (strncmp(new_name, "/var/", 5) == 0) { |
422 | entry->var_dir = 1; | 459 | entry->var_dir = 1; |
423 | var_dir = 1; | 460 | var_dir = 1; |
@@ -453,6 +490,16 @@ void fs_whitelist(void) { | |||
453 | goto errexit; | 490 | goto errexit; |
454 | } | 491 | } |
455 | } | 492 | } |
493 | else if (strncmp(new_name, "/srv/", 5) == 0) { | ||
494 | entry->srv_dir = 1; | ||
495 | srv_dir = 1; | ||
496 | // both path and absolute path are under /srv | ||
497 | if (strncmp(fname, "/srv/", 5) != 0) { | ||
498 | if (arg_debug) | ||
499 | fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); | ||
500 | goto errexit; | ||
501 | } | ||
502 | } | ||
456 | else { | 503 | else { |
457 | if (arg_debug) | 504 | if (arg_debug) |
458 | fprintf(stderr, "Debug %d: \n", __LINE__); | 505 | fprintf(stderr, "Debug %d: \n", __LINE__); |
@@ -480,21 +527,10 @@ void fs_whitelist(void) { | |||
480 | entry = entry->next; | 527 | entry = entry->next; |
481 | } | 528 | } |
482 | 529 | ||
483 | // create mount points | ||
484 | fs_build_mnt_dir(); | ||
485 | |||
486 | |||
487 | // /home/user | 530 | // /home/user |
488 | if (home_dir) { | 531 | if (home_dir) { |
489 | // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR | 532 | // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR |
490 | int rv = mkdir(RUN_WHITELIST_HOME_USER_DIR, 0755); | 533 | mkdir_attr(RUN_WHITELIST_HOME_USER_DIR, 0755, getuid(), getgid()); |
491 | if (rv == -1) | ||
492 | errExit("mkdir"); | ||
493 | if (chown(RUN_WHITELIST_HOME_USER_DIR, getuid(), getgid()) < 0) | ||
494 | errExit("chown"); | ||
495 | if (chmod(RUN_WHITELIST_HOME_USER_DIR, 0755) < 0) | ||
496 | errExit("chmod"); | ||
497 | |||
498 | if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 534 | if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
499 | errExit("mount bind"); | 535 | errExit("mount bind"); |
500 | 536 | ||
@@ -504,15 +540,8 @@ void fs_whitelist(void) { | |||
504 | 540 | ||
505 | // /tmp mountpoint | 541 | // /tmp mountpoint |
506 | if (tmp_dir) { | 542 | if (tmp_dir) { |
507 | // keep a copy of real /tmp directory in WHITELIST_TMP_DIR | 543 | // keep a copy of real /tmp directory in |
508 | int rv = mkdir(RUN_WHITELIST_TMP_DIR, 1777); | 544 | mkdir_attr(RUN_WHITELIST_TMP_DIR, 1777, 0, 0); |
509 | if (rv == -1) | ||
510 | errExit("mkdir"); | ||
511 | if (chown(RUN_WHITELIST_TMP_DIR, 0, 0) < 0) | ||
512 | errExit("chown"); | ||
513 | if (chmod(RUN_WHITELIST_TMP_DIR, 1777) < 0) | ||
514 | errExit("chmod"); | ||
515 | |||
516 | if (mount("/tmp", RUN_WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 545 | if (mount("/tmp", RUN_WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
517 | errExit("mount bind"); | 546 | errExit("mount bind"); |
518 | 547 | ||
@@ -526,37 +555,51 @@ void fs_whitelist(void) { | |||
526 | 555 | ||
527 | // /media mountpoint | 556 | // /media mountpoint |
528 | if (media_dir) { | 557 | if (media_dir) { |
529 | // keep a copy of real /media directory in RUN_WHITELIST_MEDIA_DIR | 558 | // some distros don't have a /media directory |
530 | int rv = mkdir(RUN_WHITELIST_MEDIA_DIR, 0755); | 559 | struct stat s; |
531 | if (rv == -1) | 560 | if (stat("/media", &s) == 0) { |
532 | errExit("mkdir"); | 561 | // keep a copy of real /media directory in RUN_WHITELIST_MEDIA_DIR |
533 | if (chown(RUN_WHITELIST_MEDIA_DIR, 0, 0) < 0) | 562 | mkdir_attr(RUN_WHITELIST_MEDIA_DIR, 0755, 0, 0); |
534 | errExit("chown"); | 563 | if (mount("/media", RUN_WHITELIST_MEDIA_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
535 | if (chmod(RUN_WHITELIST_MEDIA_DIR, 0755) < 0) | 564 | errExit("mount bind"); |
536 | errExit("chmod"); | ||
537 | 565 | ||
538 | if (mount("/media", RUN_WHITELIST_MEDIA_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 566 | // mount tmpfs on /media |
539 | errExit("mount bind"); | 567 | if (arg_debug || arg_debug_whitelists) |
540 | 568 | printf("Mounting tmpfs on /media directory\n"); | |
541 | // mount tmpfs on /media | 569 | if (mount("tmpfs", "/media", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
542 | if (arg_debug || arg_debug_whitelists) | 570 | errExit("mounting tmpfs on /media"); |
543 | printf("Mounting tmpfs on /media directory\n"); | 571 | fs_logger("tmpfs /media"); |
544 | if (mount("tmpfs", "/media", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 572 | } |
545 | errExit("mounting tmpfs on /media"); | 573 | else |
546 | fs_logger("tmpfs /media"); | 574 | media_dir = 0; |
575 | } | ||
576 | |||
577 | // /mnt mountpoint | ||
578 | if (mnt_dir) { | ||
579 | // check if /mnt directory exists | ||
580 | struct stat s; | ||
581 | if (stat("/mnt", &s) == 0) { | ||
582 | // keep a copy of real /mnt directory in RUN_WHITELIST_MNT_DIR | ||
583 | mkdir_attr(RUN_WHITELIST_MNT_DIR, 0755, 0, 0); | ||
584 | if (mount("/mnt", RUN_WHITELIST_MNT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
585 | errExit("mount bind"); | ||
586 | |||
587 | // mount tmpfs on /mnt | ||
588 | if (arg_debug || arg_debug_whitelists) | ||
589 | printf("Mounting tmpfs on /mnt directory\n"); | ||
590 | if (mount("tmpfs", "/mnt", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
591 | errExit("mounting tmpfs on /mnt"); | ||
592 | fs_logger("tmpfs /mnt"); | ||
593 | } | ||
594 | else | ||
595 | mnt_dir = 0; | ||
547 | } | 596 | } |
548 | 597 | ||
598 | |||
549 | // /var mountpoint | 599 | // /var mountpoint |
550 | if (var_dir) { | 600 | if (var_dir) { |
551 | // keep a copy of real /var directory in RUN_WHITELIST_VAR_DIR | 601 | // keep a copy of real /var directory in RUN_WHITELIST_VAR_DIR |
552 | int rv = mkdir(RUN_WHITELIST_VAR_DIR, 0755); | 602 | mkdir_attr(RUN_WHITELIST_VAR_DIR, 0755, 0, 0); |
553 | if (rv == -1) | ||
554 | errExit("mkdir"); | ||
555 | if (chown(RUN_WHITELIST_VAR_DIR, 0, 0) < 0) | ||
556 | errExit("chown"); | ||
557 | if (chmod(RUN_WHITELIST_VAR_DIR, 0755) < 0) | ||
558 | errExit("chmod"); | ||
559 | |||
560 | if (mount("/var", RUN_WHITELIST_VAR_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 603 | if (mount("/var", RUN_WHITELIST_VAR_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
561 | errExit("mount bind"); | 604 | errExit("mount bind"); |
562 | 605 | ||
@@ -571,14 +614,7 @@ void fs_whitelist(void) { | |||
571 | // /dev mountpoint | 614 | // /dev mountpoint |
572 | if (dev_dir) { | 615 | if (dev_dir) { |
573 | // keep a copy of real /dev directory in RUN_WHITELIST_DEV_DIR | 616 | // keep a copy of real /dev directory in RUN_WHITELIST_DEV_DIR |
574 | int rv = mkdir(RUN_WHITELIST_DEV_DIR, 0755); | 617 | mkdir_attr(RUN_WHITELIST_DEV_DIR, 0755, 0, 0); |
575 | if (rv == -1) | ||
576 | errExit("mkdir"); | ||
577 | if (chown(RUN_WHITELIST_DEV_DIR, 0, 0) < 0) | ||
578 | errExit("chown"); | ||
579 | if (chmod(RUN_WHITELIST_DEV_DIR, 0755) < 0) | ||
580 | errExit("chmod"); | ||
581 | |||
582 | if (mount("/dev", RUN_WHITELIST_DEV_DIR, NULL, MS_BIND|MS_REC, "mode=755,gid=0") < 0) | 618 | if (mount("/dev", RUN_WHITELIST_DEV_DIR, NULL, MS_BIND|MS_REC, "mode=755,gid=0") < 0) |
583 | errExit("mount bind"); | 619 | errExit("mount bind"); |
584 | 620 | ||
@@ -593,14 +629,7 @@ void fs_whitelist(void) { | |||
593 | // /opt mountpoint | 629 | // /opt mountpoint |
594 | if (opt_dir) { | 630 | if (opt_dir) { |
595 | // keep a copy of real /opt directory in RUN_WHITELIST_OPT_DIR | 631 | // keep a copy of real /opt directory in RUN_WHITELIST_OPT_DIR |
596 | int rv = mkdir(RUN_WHITELIST_OPT_DIR, 0755); | 632 | mkdir_attr(RUN_WHITELIST_OPT_DIR, 0755, 0, 0); |
597 | if (rv == -1) | ||
598 | errExit("mkdir"); | ||
599 | if (chown(RUN_WHITELIST_OPT_DIR, 0, 0) < 0) | ||
600 | errExit("chown"); | ||
601 | if (chmod(RUN_WHITELIST_OPT_DIR, 0755) < 0) | ||
602 | errExit("chmod"); | ||
603 | |||
604 | if (mount("/opt", RUN_WHITELIST_OPT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 633 | if (mount("/opt", RUN_WHITELIST_OPT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
605 | errExit("mount bind"); | 634 | errExit("mount bind"); |
606 | 635 | ||
@@ -612,6 +641,29 @@ void fs_whitelist(void) { | |||
612 | fs_logger("tmpfs /opt"); | 641 | fs_logger("tmpfs /opt"); |
613 | } | 642 | } |
614 | 643 | ||
644 | // /srv mountpoint | ||
645 | if (srv_dir) { | ||
646 | // check if /srv directory exists | ||
647 | struct stat s; | ||
648 | if (stat("/srv", &s) == 0) { | ||
649 | // keep a copy of real /srv directory in RUN_WHITELIST_SRV_DIR | ||
650 | mkdir_attr(RUN_WHITELIST_SRV_DIR, 0755, 0, 0); | ||
651 | if (mount("/srv", RUN_WHITELIST_SRV_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
652 | errExit("mount bind"); | ||
653 | |||
654 | // mount tmpfs on /srv | ||
655 | if (arg_debug || arg_debug_whitelists) | ||
656 | printf("Mounting tmpfs on /srv directory\n"); | ||
657 | if (mount("tmpfs", "/srv", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
658 | errExit("mounting tmpfs on /srv"); | ||
659 | fs_logger("tmpfs /srv"); | ||
660 | } | ||
661 | else | ||
662 | srv_dir = 0; | ||
663 | } | ||
664 | |||
665 | |||
666 | |||
615 | // go through profile rules again, and interpret whitelist commands | 667 | // go through profile rules again, and interpret whitelist commands |
616 | entry = cfg.profile; | 668 | entry = cfg.profile; |
617 | while (entry) { | 669 | while (entry) { |
@@ -696,6 +748,20 @@ void fs_whitelist(void) { | |||
696 | fs_logger2("tmpfs", RUN_WHITELIST_MEDIA_DIR); | 748 | fs_logger2("tmpfs", RUN_WHITELIST_MEDIA_DIR); |
697 | } | 749 | } |
698 | 750 | ||
751 | // mask the real /mnt directory, currently mounted on RUN_WHITELIST_MNT_DIR | ||
752 | if (mnt_dir) { | ||
753 | if (mount("tmpfs", RUN_WHITELIST_MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
754 | errExit("mount tmpfs"); | ||
755 | fs_logger2("tmpfs", RUN_WHITELIST_MNT_DIR); | ||
756 | } | ||
757 | |||
758 | // mask the real /srv directory, currently mounted on RUN_WHITELIST_SRV_DIR | ||
759 | if (srv_dir) { | ||
760 | if (mount("tmpfs", RUN_WHITELIST_SRV_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
761 | errExit("mount tmpfs"); | ||
762 | fs_logger2("tmpfs", RUN_WHITELIST_SRV_DIR); | ||
763 | } | ||
764 | |||
699 | if (new_name) | 765 | if (new_name) |
700 | free(new_name); | 766 | free(new_name); |
701 | 767 | ||
diff --git a/src/firejail/join.c b/src/firejail/join.c index 98e140ce4..628002d35 100644 --- a/src/firejail/join.c +++ b/src/firejail/join.c | |||
@@ -23,12 +23,19 @@ | |||
23 | #include <fcntl.h> | 23 | #include <fcntl.h> |
24 | #include <unistd.h> | 24 | #include <unistd.h> |
25 | #include <sys/prctl.h> | 25 | #include <sys/prctl.h> |
26 | #include <errno.h> | ||
26 | 27 | ||
27 | static int apply_caps = 0; | 28 | static int apply_caps = 0; |
28 | static uint64_t caps = 0; | 29 | static uint64_t caps = 0; |
29 | static int apply_seccomp = 0; | 30 | static int apply_seccomp = 0; |
30 | #define BUFLEN 4096 | 31 | #define BUFLEN 4096 |
31 | 32 | ||
33 | static void signal_handler(int sig){ | ||
34 | flush_stdin(); | ||
35 | |||
36 | exit(sig); | ||
37 | } | ||
38 | |||
32 | static void extract_command(int argc, char **argv, int index) { | 39 | static void extract_command(int argc, char **argv, int index) { |
33 | EUID_ASSERT(); | 40 | EUID_ASSERT(); |
34 | if (index >= argc) | 41 | if (index >= argc) |
@@ -48,22 +55,9 @@ static void extract_command(int argc, char **argv, int index) { | |||
48 | exit(1); | 55 | exit(1); |
49 | } | 56 | } |
50 | 57 | ||
51 | |||
52 | int len = 0; | ||
53 | int i; | ||
54 | // calculate command length | ||
55 | for (i = index; i < argc; i++) { | ||
56 | len += strlen(argv[i]) + 1; | ||
57 | } | ||
58 | assert(len > 0); | ||
59 | |||
60 | // build command | 58 | // build command |
61 | cfg.command_line = malloc(len + 1); | 59 | build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, index); |
62 | *cfg.command_line = '\0'; | 60 | |
63 | for (i = index; i < argc; i++) { | ||
64 | strcat(cfg.command_line, argv[i]); | ||
65 | strcat(cfg.command_line, " "); | ||
66 | } | ||
67 | if (arg_debug) | 61 | if (arg_debug) |
68 | printf("Extracted command #%s#\n", cfg.command_line); | 62 | printf("Extracted command #%s#\n", cfg.command_line); |
69 | } | 63 | } |
@@ -134,7 +128,7 @@ static void extract_caps_seccomp(pid_t pid) { | |||
134 | break; | 128 | break; |
135 | } | 129 | } |
136 | else if (strncmp(buf, "CapBnd:", 7) == 0) { | 130 | else if (strncmp(buf, "CapBnd:", 7) == 0) { |
137 | char *ptr = buf + 8; | 131 | char *ptr = buf + 7; |
138 | unsigned long long val; | 132 | unsigned long long val; |
139 | sscanf(ptr, "%llx", &val); | 133 | sscanf(ptr, "%llx", &val); |
140 | apply_caps = 1; | 134 | apply_caps = 1; |
@@ -179,26 +173,12 @@ static void extract_user_namespace(pid_t pid) { | |||
179 | free(uidmap); | 173 | free(uidmap); |
180 | } | 174 | } |
181 | 175 | ||
182 | void join_name(const char *name, int argc, char **argv, int index) { | ||
183 | EUID_ASSERT(); | ||
184 | if (!name || strlen(name) == 0) { | ||
185 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
186 | exit(1); | ||
187 | } | ||
188 | |||
189 | pid_t pid; | ||
190 | if (name2pid(name, &pid)) { | ||
191 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
192 | exit(1); | ||
193 | } | ||
194 | join(pid, argc, argv, index); | ||
195 | } | ||
196 | |||
197 | void join(pid_t pid, int argc, char **argv, int index) { | 176 | void join(pid_t pid, int argc, char **argv, int index) { |
198 | EUID_ASSERT(); | 177 | EUID_ASSERT(); |
199 | char *homedir = cfg.homedir; | 178 | char *homedir = cfg.homedir; |
200 | 179 | ||
201 | extract_command(argc, argv, index); | 180 | extract_command(argc, argv, index); |
181 | signal (SIGTERM, signal_handler); | ||
202 | 182 | ||
203 | // if the pid is that of a firejail process, use the pid of the first child process | 183 | // if the pid is that of a firejail process, use the pid of the first child process |
204 | EUID_ROOT(); | 184 | EUID_ROOT(); |
@@ -249,15 +229,11 @@ void join(pid_t pid, int argc, char **argv, int index) { | |||
249 | exit(1); | 229 | exit(1); |
250 | } | 230 | } |
251 | else { | 231 | else { |
252 | if (join_namespace(pid, "ipc")) | 232 | if (join_namespace(pid, "ipc") || |
253 | exit(1); | 233 | join_namespace(pid, "net") || |
254 | if (join_namespace(pid, "net")) | 234 | join_namespace(pid, "pid") || |
255 | exit(1); | 235 | join_namespace(pid, "uts") || |
256 | if (join_namespace(pid, "pid")) | 236 | join_namespace(pid, "mnt")) |
257 | exit(1); | ||
258 | if (join_namespace(pid, "uts")) | ||
259 | exit(1); | ||
260 | if (join_namespace(pid, "mnt")) | ||
261 | exit(1); | 237 | exit(1); |
262 | } | 238 | } |
263 | 239 | ||
@@ -297,19 +273,18 @@ void join(pid_t pid, int argc, char **argv, int index) { | |||
297 | if (apply_caps == 1) // not available for uid 0 | 273 | if (apply_caps == 1) // not available for uid 0 |
298 | caps_set(caps); | 274 | caps_set(caps); |
299 | #ifdef HAVE_SECCOMP | 275 | #ifdef HAVE_SECCOMP |
300 | // set protocol filter | 276 | // read cfg.protocol from file |
301 | if (getuid() != 0) | 277 | if (getuid() != 0) |
302 | protocol_filter_load(RUN_PROTOCOL_CFG); | 278 | protocol_filter_load(RUN_PROTOCOL_CFG); |
303 | if (cfg.protocol) { // not available for uid 0 | 279 | if (cfg.protocol) { // not available for uid 0 |
304 | protocol_filter(); | 280 | seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter |
305 | } | 281 | } |
306 | 282 | ||
307 | // set seccomp filter | 283 | // set seccomp filter |
308 | if (apply_seccomp == 1) // not available for uid 0 | 284 | if (apply_seccomp == 1) // not available for uid 0 |
309 | seccomp_set(); | 285 | seccomp_load(RUN_SECCOMP_CFG); |
310 | |||
311 | #endif | 286 | #endif |
312 | 287 | ||
313 | // fix qt 4.8 | 288 | // fix qt 4.8 |
314 | if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0) | 289 | if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0) |
315 | errExit("setenv"); | 290 | errExit("setenv"); |
@@ -322,76 +297,84 @@ void join(pid_t pid, int argc, char **argv, int index) { | |||
322 | printf("Joining user namespace\n"); | 297 | printf("Joining user namespace\n"); |
323 | if (join_namespace(1, "user")) | 298 | if (join_namespace(1, "user")) |
324 | exit(1); | 299 | exit(1); |
300 | |||
301 | // user namespace resets capabilities | ||
302 | // set caps filter | ||
303 | if (apply_caps == 1) // not available for uid 0 | ||
304 | caps_set(caps); | ||
325 | } | 305 | } |
326 | else | 306 | else |
327 | drop_privs(arg_nogroups); // nogroups not available for uid 0 | 307 | drop_privs(arg_nogroups); // nogroups not available for uid 0 |
328 | 308 | ||
309 | |||
329 | // set prompt color to green | 310 | // set prompt color to green |
330 | //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' | 311 | char *prompt = getenv("FIREJAIL_PROMPT"); |
331 | if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) | 312 | if (prompt && strcmp(prompt, "yes") == 0) { |
332 | errExit("setenv"); | 313 | //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' |
314 | if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) | ||
315 | errExit("setenv"); | ||
316 | } | ||
317 | |||
318 | // set nice | ||
319 | if (arg_nice) { | ||
320 | errno = 0; | ||
321 | int rv = nice(cfg.nice); | ||
322 | (void) rv; | ||
323 | if (errno) { | ||
324 | fprintf(stderr, "Warning: cannot set nice value\n"); | ||
325 | errno = 0; | ||
326 | } | ||
327 | } | ||
333 | 328 | ||
334 | // run cmdline trough /bin/bash | 329 | // run cmdline trough shell |
335 | if (cfg.command_line == NULL) { | 330 | if (cfg.command_line == NULL) { |
331 | // if the sandbox was started with --shell=none, it is possible we don't have a shell | ||
332 | // inside the sandbox | ||
333 | if (cfg.shell == NULL) { | ||
334 | cfg.shell = guess_shell(); | ||
335 | if (!cfg.shell) { | ||
336 | fprintf(stderr, "Error: no POSIX shell found, please use --shell command line option\n"); | ||
337 | exit(1); | ||
338 | } | ||
339 | } | ||
340 | |||
336 | struct stat s; | 341 | struct stat s; |
342 | if (stat(cfg.shell, &s) == -1) { | ||
343 | fprintf(stderr, "Error: %s shell not found inside the sandbox\n", cfg.shell); | ||
344 | exit(1); | ||
345 | } | ||
337 | 346 | ||
338 | // replace the process with a shell | 347 | cfg.command_line = cfg.shell; |
339 | if (stat("/bin/bash", &s) == 0) | 348 | cfg.window_title = cfg.shell; |
340 | execlp("/bin/bash", "/bin/bash", NULL); | ||
341 | else if (stat("/usr/bin/zsh", &s) == 0) | ||
342 | execlp("/usr/bin/zsh", "/usr/bin/zsh", NULL); | ||
343 | else if (stat("/bin/csh", &s) == 0) | ||
344 | execlp("/bin/csh", "/bin/csh", NULL); | ||
345 | else if (stat("/bin/sh", &s) == 0) | ||
346 | execlp("/bin/sh", "/bin/sh", NULL); | ||
347 | |||
348 | // no shell found, print an error and exit | ||
349 | fprintf(stderr, "Error: no POSIX shell found\n"); | ||
350 | sleep(5); | ||
351 | exit(1); | ||
352 | } | 349 | } |
353 | else { | ||
354 | // run the command supplied by the user | ||
355 | int cwd = 0; | ||
356 | if (cfg.cwd) { | ||
357 | if (chdir(cfg.cwd) == 0) | ||
358 | cwd = 1; | ||
359 | } | ||
360 | |||
361 | if (!cwd) { | ||
362 | if (chdir("/") < 0) | ||
363 | errExit("chdir"); | ||
364 | if (cfg.homedir) { | ||
365 | struct stat s; | ||
366 | if (stat(cfg.homedir, &s) == 0) { | ||
367 | if (chdir(cfg.homedir) < 0) | ||
368 | errExit("chdir"); | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | 350 | ||
373 | char *arg[5]; | 351 | int cwd = 0; |
374 | arg[0] = "/bin/bash"; | 352 | if (cfg.cwd) { |
375 | arg[1] = "-c"; | 353 | if (chdir(cfg.cwd) == 0) |
376 | if (arg_debug) | 354 | cwd = 1; |
377 | printf("Starting %s\n", cfg.command_line); | 355 | } |
378 | if (!arg_doubledash) { | 356 | |
379 | arg[2] = cfg.command_line; | 357 | if (!cwd) { |
380 | arg[3] = NULL; | 358 | if (chdir("/") < 0) |
381 | } | 359 | errExit("chdir"); |
382 | else { | 360 | if (cfg.homedir) { |
383 | arg[2] = "--"; | 361 | struct stat s; |
384 | arg[3] = cfg.command_line; | 362 | if (stat(cfg.homedir, &s) == 0) { |
385 | arg[4] = NULL; | 363 | /* coverity[toctou] */ |
364 | if (chdir(cfg.homedir) < 0) | ||
365 | errExit("chdir"); | ||
366 | } | ||
386 | } | 367 | } |
387 | execvp("/bin/bash", arg); | ||
388 | } | 368 | } |
389 | 369 | ||
370 | start_application(); | ||
371 | |||
390 | // it will never get here!!! | 372 | // it will never get here!!! |
391 | } | 373 | } |
392 | 374 | ||
393 | // wait for the child to finish | 375 | // wait for the child to finish |
394 | waitpid(child, NULL, 0); | 376 | waitpid(child, NULL, 0); |
377 | flush_stdin(); | ||
395 | exit(0); | 378 | exit(0); |
396 | } | 379 | } |
397 | 380 | ||
diff --git a/src/firejail/list.c b/src/firejail/list.c deleted file mode 100644 index cd53264b6..000000000 --- a/src/firejail/list.c +++ /dev/null | |||
@@ -1,81 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "firejail.h" | ||
21 | #include <sys/types.h> | ||
22 | #include <sys/stat.h> | ||
23 | |||
24 | static void grsec_elevate_privileges(void) { | ||
25 | struct stat s; | ||
26 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
27 | EUID_ROOT(); | ||
28 | |||
29 | // elevate privileges | ||
30 | if (setreuid(0, 0)) | ||
31 | errExit("setreuid"); | ||
32 | if (setregid(0, 0)) | ||
33 | errExit("setregid"); | ||
34 | } | ||
35 | } | ||
36 | |||
37 | void top(void) { | ||
38 | EUID_ASSERT(); | ||
39 | |||
40 | char *arg[4]; | ||
41 | arg[0] = "bash"; | ||
42 | arg[1] = "-c"; | ||
43 | arg[2] = "firemon --top"; | ||
44 | arg[3] = NULL; | ||
45 | execvp("/bin/bash", arg); | ||
46 | } | ||
47 | |||
48 | void netstats(void) { | ||
49 | EUID_ASSERT(); | ||
50 | grsec_elevate_privileges(); | ||
51 | |||
52 | char *arg[4]; | ||
53 | arg[0] = "bash"; | ||
54 | arg[1] = "-c"; | ||
55 | arg[2] = "firemon --netstats"; | ||
56 | arg[3] = NULL; | ||
57 | execvp("/bin/bash", arg); | ||
58 | } | ||
59 | |||
60 | void list(void) { | ||
61 | EUID_ASSERT(); | ||
62 | |||
63 | char *arg[4]; | ||
64 | arg[0] = "bash"; | ||
65 | arg[1] = "-c"; | ||
66 | arg[2] = "firemon --list"; | ||
67 | arg[3] = NULL; | ||
68 | execvp("/bin/bash", arg); | ||
69 | } | ||
70 | |||
71 | void tree(void) { | ||
72 | EUID_ASSERT(); | ||
73 | |||
74 | char *arg[4]; | ||
75 | arg[0] = "bash"; | ||
76 | arg[1] = "-c"; | ||
77 | arg[2] = "firemon --tree"; | ||
78 | arg[3] = NULL; | ||
79 | execvp("/bin/bash", arg); | ||
80 | } | ||
81 | |||
diff --git a/src/firejail/ls.c b/src/firejail/ls.c index 444b5b69e..86c3a6079 100644 --- a/src/firejail/ls.c +++ b/src/firejail/ls.c | |||
@@ -185,23 +185,26 @@ static void print_directory(const char *path) { | |||
185 | free(namelist); | 185 | free(namelist); |
186 | } | 186 | } |
187 | 187 | ||
188 | void sandboxfs_name(int op, const char *name, const char *path) { | 188 | char *expand_path(const char *path) { |
189 | EUID_ASSERT(); | 189 | char *fname = NULL; |
190 | 190 | if (*path == '/') { | |
191 | if (!name || strlen(name) == 0) { | 191 | fname = strdup(path); |
192 | fprintf(stderr, "Error: invalid sandbox name\n"); | 192 | if (!fname) |
193 | exit(1); | 193 | errExit("strdup"); |
194 | } | 194 | } |
195 | pid_t pid; | 195 | else if (*path == '~') { |
196 | if (name2pid(name, &pid)) { | 196 | if (asprintf(&fname, "%s%s", cfg.homedir, path + 1) == -1) |
197 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | 197 | errExit("asprintf"); |
198 | exit(1); | ||
199 | } | 198 | } |
200 | 199 | else { | |
201 | sandboxfs(op, pid, path); | 200 | // assume the file is in current working directory |
201 | if (asprintf(&fname, "%s/%s", cfg.cwd, path) == -1) | ||
202 | errExit("asprintf"); | ||
203 | } | ||
204 | return fname; | ||
202 | } | 205 | } |
203 | 206 | ||
204 | void sandboxfs(int op, pid_t pid, const char *path) { | 207 | void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) { |
205 | EUID_ASSERT(); | 208 | EUID_ASSERT(); |
206 | 209 | ||
207 | // if the pid is that of a firejail process, use the pid of the first child process | 210 | // if the pid is that of a firejail process, use the pid of the first child process |
@@ -228,22 +231,17 @@ void sandboxfs(int op, pid_t pid, const char *path) { | |||
228 | } | 231 | } |
229 | } | 232 | } |
230 | 233 | ||
231 | // full path or file in current directory? | 234 | // expand paths |
232 | char *fname; | 235 | char *fname1 = expand_path(path1);; |
233 | if (*path == '/') { | 236 | char *fname2 = NULL; |
234 | fname = strdup(path); | 237 | if (path2 != NULL) { |
235 | if (!fname) | 238 | fname2 = expand_path(path2); |
236 | errExit("strdup"); | ||
237 | } | ||
238 | else if (*path == '~') { | ||
239 | if (asprintf(&fname, "%s%s", cfg.homedir, path + 1) == -1) | ||
240 | errExit("asprintf"); | ||
241 | } | 239 | } |
242 | else { | 240 | if (arg_debug) { |
243 | fprintf(stderr, "Error: Cannot access %s\n", path); | 241 | printf("file1 %s\n", fname1); |
244 | exit(1); | 242 | printf("file2 %s\n", fname2); |
245 | } | 243 | } |
246 | 244 | ||
247 | // sandbox root directory | 245 | // sandbox root directory |
248 | char *rootdir; | 246 | char *rootdir; |
249 | if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) | 247 | if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) |
@@ -257,22 +255,24 @@ void sandboxfs(int op, pid_t pid, const char *path) { | |||
257 | if (chdir("/") < 0) | 255 | if (chdir("/") < 0) |
258 | errExit("chdir"); | 256 | errExit("chdir"); |
259 | 257 | ||
260 | // access chek is performed with the real UID | 258 | // drop privileges |
261 | if (access(fname, R_OK) == -1) { | 259 | drop_privs(0); |
262 | fprintf(stderr, "Error: Cannot access %s\n", fname); | 260 | |
261 | if (access(fname1, R_OK) == -1) { | ||
262 | fprintf(stderr, "Error: Cannot access %s\n", fname1); | ||
263 | exit(1); | 263 | exit(1); |
264 | } | 264 | } |
265 | 265 | ||
266 | // list directory contents | 266 | // list directory contents |
267 | struct stat s; | 267 | struct stat s; |
268 | if (stat(fname, &s) == -1) { | 268 | if (stat(fname1, &s) == -1) { |
269 | fprintf(stderr, "Error: Cannot access %s\n", fname); | 269 | fprintf(stderr, "Error: Cannot access %s\n", fname1); |
270 | exit(1); | 270 | exit(1); |
271 | } | 271 | } |
272 | if (S_ISDIR(s.st_mode)) { | 272 | if (S_ISDIR(s.st_mode)) { |
273 | char *rp = realpath(fname, NULL); | 273 | char *rp = realpath(fname1, NULL); |
274 | if (!rp) { | 274 | if (!rp) { |
275 | fprintf(stderr, "Error: Cannot access %s\n", fname); | 275 | fprintf(stderr, "Error: Cannot access %s\n", fname1); |
276 | exit(1); | 276 | exit(1); |
277 | } | 277 | } |
278 | if (arg_debug) | 278 | if (arg_debug) |
@@ -287,9 +287,9 @@ void sandboxfs(int op, pid_t pid, const char *path) { | |||
287 | free(dir); | 287 | free(dir); |
288 | } | 288 | } |
289 | else { | 289 | else { |
290 | char *rp = realpath(fname, NULL); | 290 | char *rp = realpath(fname1, NULL); |
291 | if (!rp) { | 291 | if (!rp) { |
292 | fprintf(stderr, "Error: Cannot access %s\n", fname); | 292 | fprintf(stderr, "Error: Cannot access %s\n", fname1); |
293 | exit(1); | 293 | exit(1); |
294 | } | 294 | } |
295 | if (arg_debug) | 295 | if (arg_debug) |
@@ -308,19 +308,24 @@ void sandboxfs(int op, pid_t pid, const char *path) { | |||
308 | 308 | ||
309 | // get file from sandbox and store it in the current directory | 309 | // get file from sandbox and store it in the current directory |
310 | else if (op == SANDBOX_FS_GET) { | 310 | else if (op == SANDBOX_FS_GET) { |
311 | // check source file (sandbox) | 311 | char *src_fname =fname1; |
312 | char *src_fname; | 312 | char *dest_fname = strrchr(fname1, '/'); |
313 | if (asprintf(&src_fname, "%s%s", rootdir, fname) == -1) | 313 | if (!dest_fname || *(++dest_fname) == '\0') { |
314 | errExit("asprintf"); | 314 | fprintf(stderr, "Error: invalid file name %s\n", fname1); |
315 | EUID_ROOT(); | ||
316 | struct stat s; | ||
317 | if (stat(src_fname, &s) == -1) { | ||
318 | fprintf(stderr, "Error: Cannot access %s\n", fname); | ||
319 | exit(1); | 315 | exit(1); |
320 | } | 316 | } |
321 | 317 | ||
322 | 318 | EUID_ROOT(); | |
323 | // try to open the source file - we need to chroot | 319 | if (arg_debug) |
320 | printf("copy %s to %s\n", src_fname, dest_fname); | ||
321 | |||
322 | // create a user-owned temporary file in /run/firejail directory | ||
323 | char tmp_fname[] = "/run/firejail/tmpget-XXXXXX"; | ||
324 | int fd = mkstemp(tmp_fname); | ||
325 | SET_PERMS_FD(fd, getuid(), getgid(), 0600); | ||
326 | close(fd); | ||
327 | |||
328 | // copy the source file into the temporary file - we need to chroot | ||
324 | pid_t child = fork(); | 329 | pid_t child = fork(); |
325 | if (child < 0) | 330 | if (child < 0) |
326 | errExit("fork"); | 331 | errExit("fork"); |
@@ -334,55 +339,135 @@ void sandboxfs(int op, pid_t pid, const char *path) { | |||
334 | // drop privileges | 339 | // drop privileges |
335 | drop_privs(0); | 340 | drop_privs(0); |
336 | 341 | ||
337 | // try to read the file | 342 | // copy the file |
338 | if (access(fname, R_OK) == -1) { | 343 | if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) |
339 | fprintf(stderr, "Error: Cannot read %s\n", fname); | 344 | _exit(1); |
340 | exit(1); | 345 | #ifdef HAVE_GCOV |
341 | } | 346 | __gcov_flush(); |
342 | exit(0); | 347 | #endif |
348 | _exit(0); | ||
343 | } | 349 | } |
344 | 350 | ||
345 | // wait for the child to finish | 351 | // wait for the child to finish |
346 | int status = 0; | 352 | int status = 0; |
347 | waitpid(child, &status, 0); | 353 | waitpid(child, &status, 0); |
348 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0); | 354 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0); |
349 | else | 355 | else { |
356 | unlink(tmp_fname); | ||
350 | exit(1); | 357 | exit(1); |
351 | EUID_USER(); | 358 | } |
352 | 359 | ||
353 | // check destination file (host) | 360 | // copy the temporary file into the destionation file |
354 | char *dest_fname = strrchr(fname, '/'); | 361 | child = fork(); |
355 | if (!dest_fname || *(++dest_fname) == '\0') { | 362 | if (child < 0) |
356 | fprintf(stderr, "Error: invalid file name %s\n", fname); | 363 | errExit("fork"); |
364 | if (child == 0) { | ||
365 | // drop privileges | ||
366 | drop_privs(0); | ||
367 | |||
368 | // copy the file | ||
369 | if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) | ||
370 | _exit(1); | ||
371 | #ifdef HAVE_GCOV | ||
372 | __gcov_flush(); | ||
373 | #endif | ||
374 | _exit(0); | ||
375 | } | ||
376 | |||
377 | // wait for the child to finish | ||
378 | status = 0; | ||
379 | waitpid(child, &status, 0); | ||
380 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0); | ||
381 | else { | ||
382 | unlink(tmp_fname); | ||
357 | exit(1); | 383 | exit(1); |
358 | } | 384 | } |
359 | 385 | ||
360 | if (access(dest_fname, F_OK) == -1) { | 386 | // remove the temporary file |
361 | // try to create the file | 387 | unlink(tmp_fname); |
362 | FILE *fp = fopen(dest_fname, "w"); | 388 | EUID_USER(); |
363 | if (!fp) { | 389 | } |
364 | fprintf(stderr, "Error: cannot create %s\n", dest_fname); | 390 | // get file from host and store it in the sandbox |
365 | exit(1); | 391 | else if (op == SANDBOX_FS_PUT && path2) { |
366 | } | 392 | char *src_fname =fname1; |
367 | fclose(fp); | 393 | char *dest_fname = fname2; |
394 | |||
395 | EUID_ROOT(); | ||
396 | if (arg_debug) | ||
397 | printf("copy %s to %s\n", src_fname, dest_fname); | ||
398 | |||
399 | // create a user-owned temporary file in /run/firejail directory | ||
400 | char tmp_fname[] = "/run/firejail/tmpget-XXXXXX"; | ||
401 | int fd = mkstemp(tmp_fname); | ||
402 | SET_PERMS_FD(fd, getuid(), getgid(), 0600); | ||
403 | close(fd); | ||
404 | |||
405 | // copy the source file into the temporary file - we need to chroot | ||
406 | pid_t child = fork(); | ||
407 | if (child < 0) | ||
408 | errExit("fork"); | ||
409 | if (child == 0) { | ||
410 | // drop privileges | ||
411 | drop_privs(0); | ||
412 | |||
413 | // copy the file | ||
414 | if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) | ||
415 | _exit(1); | ||
416 | #ifdef HAVE_GCOV | ||
417 | __gcov_flush(); | ||
418 | #endif | ||
419 | _exit(0); | ||
368 | } | 420 | } |
421 | |||
422 | // wait for the child to finish | ||
423 | int status = 0; | ||
424 | waitpid(child, &status, 0); | ||
425 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0); | ||
369 | else { | 426 | else { |
370 | if (access(dest_fname, W_OK) == -1) { | 427 | unlink(tmp_fname); |
371 | fprintf(stderr, "Error: cannot write %s\n", dest_fname); | 428 | exit(1); |
372 | exit(1); | ||
373 | } | ||
374 | } | 429 | } |
375 | // copy file | 430 | |
376 | EUID_ROOT(); | 431 | // copy the temporary file into the destionation file |
377 | copy_file(src_fname, dest_fname); | 432 | child = fork(); |
378 | if (chown(dest_fname, getuid(), getgid()) == -1) | 433 | if (child < 0) |
379 | errExit("chown"); | 434 | errExit("fork"); |
380 | if (chmod(dest_fname, 0644) == -1) | 435 | if (child == 0) { |
381 | errExit("chmod"); | 436 | // chroot |
437 | if (chroot(rootdir) < 0) | ||
438 | errExit("chroot"); | ||
439 | if (chdir("/") < 0) | ||
440 | errExit("chdir"); | ||
441 | |||
442 | // drop privileges | ||
443 | drop_privs(0); | ||
444 | |||
445 | // copy the file | ||
446 | if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) | ||
447 | _exit(1); | ||
448 | #ifdef HAVE_GCOV | ||
449 | __gcov_flush(); | ||
450 | #endif | ||
451 | _exit(0); | ||
452 | } | ||
453 | |||
454 | // wait for the child to finish | ||
455 | status = 0; | ||
456 | waitpid(child, &status, 0); | ||
457 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0); | ||
458 | else { | ||
459 | unlink(tmp_fname); | ||
460 | exit(1); | ||
461 | } | ||
462 | |||
463 | // remove the temporary file | ||
464 | unlink(tmp_fname); | ||
382 | EUID_USER(); | 465 | EUID_USER(); |
383 | } | 466 | } |
384 | 467 | ||
385 | free(fname); | 468 | if (fname2) |
469 | free(fname2); | ||
470 | free(fname1); | ||
386 | free(rootdir); | 471 | free(rootdir); |
387 | 472 | ||
388 | exit(0); | 473 | exit(0); |
diff --git a/src/firejail/main.c b/src/firejail/main.c index bdf960b96..ec0c31285 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -46,21 +46,22 @@ printf("time %s:%d %u\n", __FILE__, __LINE__, (uint32_t) systick); | |||
46 | #endif | 46 | #endif |
47 | 47 | ||
48 | uid_t firejail_uid = 0; | 48 | uid_t firejail_uid = 0; |
49 | gid_t firejail_gid = 0; | ||
49 | 50 | ||
50 | #define STACK_SIZE (1024 * 1024) | 51 | #define STACK_SIZE (1024 * 1024) |
51 | static char child_stack[STACK_SIZE]; // space for child's stack | 52 | static char child_stack[STACK_SIZE]; // space for child's stack |
52 | Config cfg; // configuration | 53 | Config cfg; // configuration |
53 | int arg_private = 0; // mount private /home and /tmp directoryu | 54 | int arg_private = 0; // mount private /home and /tmp directoryu |
55 | int arg_private_template = 0; // mount private /home using a template | ||
54 | int arg_debug = 0; // print debug messages | 56 | int arg_debug = 0; // print debug messages |
55 | int arg_debug_check_filename; // print debug messages for filename checking | 57 | int arg_debug_check_filename = 0; // print debug messages for filename checking |
56 | int arg_debug_blacklists; // print debug messages for blacklists | 58 | int arg_debug_blacklists = 0; // print debug messages for blacklists |
57 | int arg_debug_whitelists; // print debug messages for whitelists | 59 | int arg_debug_whitelists = 0; // print debug messages for whitelists |
58 | int arg_nonetwork = 0; // --net=none | 60 | int arg_nonetwork = 0; // --net=none |
59 | int arg_command = 0; // -c | 61 | int arg_command = 0; // -c |
60 | int arg_overlay = 0; // overlay option | 62 | int arg_overlay = 0; // overlay option |
61 | int arg_overlay_keep = 0; // place overlay diff directory in ~/.firejail | 63 | int arg_overlay_keep = 0; // place overlay diff in a known directory |
62 | int arg_zsh = 0; // use zsh as default shell | 64 | int arg_overlay_reuse = 0; // allow the reuse of overlays |
63 | int arg_csh = 0; // use csh as default shell | ||
64 | 65 | ||
65 | int arg_seccomp = 0; // enable default seccomp filter | 66 | int arg_seccomp = 0; // enable default seccomp filter |
66 | 67 | ||
@@ -77,6 +78,7 @@ int arg_rlimit_nproc = 0; // rlimit nproc | |||
77 | int arg_rlimit_fsize = 0; // rlimit fsize | 78 | int arg_rlimit_fsize = 0; // rlimit fsize |
78 | int arg_rlimit_sigpending = 0; // rlimit fsize | 79 | int arg_rlimit_sigpending = 0; // rlimit fsize |
79 | int arg_nogroups = 0; // disable supplementary groups | 80 | int arg_nogroups = 0; // disable supplementary groups |
81 | int arg_nonewprivs = 0; // set the NO_NEW_PRIVS prctl | ||
80 | int arg_noroot = 0; // create a new user namespace and disable root user | 82 | int arg_noroot = 0; // create a new user namespace and disable root user |
81 | int arg_netfilter; // enable netfilter | 83 | int arg_netfilter; // enable netfilter |
82 | int arg_netfilter6; // enable netfilter6 | 84 | int arg_netfilter6; // enable netfilter6 |
@@ -91,11 +93,25 @@ int arg_private_tmp = 0; // private tmp directory | |||
91 | int arg_scan = 0; // arp-scan all interfaces | 93 | int arg_scan = 0; // arp-scan all interfaces |
92 | int arg_whitelist = 0; // whitelist commad | 94 | int arg_whitelist = 0; // whitelist commad |
93 | int arg_nosound = 0; // disable sound | 95 | int arg_nosound = 0; // disable sound |
96 | int arg_no3d; // disable 3d hardware acceleration | ||
94 | int arg_quiet = 0; // no output for scripting | 97 | int arg_quiet = 0; // no output for scripting |
95 | int arg_join_network = 0; // join only the network namespace | 98 | int arg_join_network = 0; // join only the network namespace |
96 | int arg_join_filesystem = 0; // join only the mount namespace | 99 | int arg_join_filesystem = 0; // join only the mount namespace |
97 | int arg_nice = 0; // nice value configured | 100 | int arg_nice = 0; // nice value configured |
98 | int arg_ipc = 0; // enable ipc namespace | 101 | int arg_ipc = 0; // enable ipc namespace |
102 | int arg_writable_etc = 0; // writable etc | ||
103 | int arg_writable_var = 0; // writable var | ||
104 | int arg_appimage = 0; // appimage | ||
105 | int arg_audit = 0; // audit | ||
106 | char *arg_audit_prog = NULL; // audit | ||
107 | int arg_apparmor = 0; // apparmor | ||
108 | int arg_allow_debuggers = 0; // allow debuggers | ||
109 | int arg_x11_block = 0; // block X11 | ||
110 | int arg_x11_xorg = 0; // use X11 security extention | ||
111 | int arg_allusers = 0; // all user home directories visible | ||
112 | |||
113 | int login_shell = 0; | ||
114 | |||
99 | 115 | ||
100 | int parent_to_child_fds[2]; | 116 | int parent_to_child_fds[2]; |
101 | int child_to_parent_fds[2]; | 117 | int child_to_parent_fds[2]; |
@@ -126,7 +142,8 @@ static void myexit(int rv) { | |||
126 | // delete sandbox files in shared memory | 142 | // delete sandbox files in shared memory |
127 | EUID_ROOT(); | 143 | EUID_ROOT(); |
128 | clear_run_files(sandbox_pid); | 144 | clear_run_files(sandbox_pid); |
129 | 145 | appimage_clear(); | |
146 | flush_stdin(); | ||
130 | exit(rv); | 147 | exit(rv); |
131 | } | 148 | } |
132 | 149 | ||
@@ -141,20 +158,37 @@ static void my_handler(int s){ | |||
141 | myexit(1); | 158 | myexit(1); |
142 | } | 159 | } |
143 | 160 | ||
144 | // return 1 if error, 0 if a valid pid was found | 161 | static pid_t extract_pid(const char *name) { |
145 | static inline int read_pid(char *str, pid_t *pid) { | 162 | EUID_ASSERT(); |
163 | if (!name || strlen(name) == 0) { | ||
164 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
165 | exit(1); | ||
166 | } | ||
167 | |||
168 | pid_t pid; | ||
169 | EUID_ROOT(); | ||
170 | if (name2pid(name, &pid)) { | ||
171 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
172 | exit(1); | ||
173 | } | ||
174 | EUID_USER(); | ||
175 | return pid; | ||
176 | } | ||
177 | |||
178 | |||
179 | static pid_t read_pid(const char *str) { | ||
146 | char *endptr; | 180 | char *endptr; |
147 | errno = 0; | 181 | errno = 0; |
148 | long int pidtmp = strtol(str, &endptr, 10); | 182 | long int pidtmp = strtol(str, &endptr, 10); |
149 | if ((errno == ERANGE && (pidtmp == LONG_MAX || pidtmp == LONG_MIN)) | 183 | if ((errno == ERANGE && (pidtmp == LONG_MAX || pidtmp == LONG_MIN)) |
150 | || (errno != 0 && pidtmp == 0)) { | 184 | || (errno != 0 && pidtmp == 0)) { |
151 | return 1; | 185 | return extract_pid(str); |
152 | } | 186 | } |
153 | if (endptr == str) { | 187 | // endptr points to '\0' char in str if the entire string is valid |
154 | return 1; | 188 | if (endptr == NULL || endptr[0]!='\0') { |
189 | return extract_pid(str); | ||
155 | } | 190 | } |
156 | *pid = (pid_t)pidtmp; | 191 | return (pid_t)pidtmp; |
157 | return 0; | ||
158 | } | 192 | } |
159 | 193 | ||
160 | // init configuration | 194 | // init configuration |
@@ -228,12 +262,14 @@ void check_user_namespace(void) { | |||
228 | stat("/proc/self/gid_map", &s3) == 0) | 262 | stat("/proc/self/gid_map", &s3) == 0) |
229 | arg_noroot = 1; | 263 | arg_noroot = 1; |
230 | else { | 264 | else { |
231 | fprintf(stderr, "Warning: user namespaces not available in the current kernel.\n"); | 265 | if (!arg_quiet || arg_debug) |
266 | fprintf(stderr, "Warning: user namespaces not available in the current kernel.\n"); | ||
232 | arg_noroot = 0; | 267 | arg_noroot = 0; |
233 | } | 268 | } |
234 | } | 269 | } |
235 | #endif | 270 | #endif |
236 | 271 | ||
272 | |||
237 | // exit commands | 273 | // exit commands |
238 | static void run_cmd_and_exit(int i, int argc, char **argv) { | 274 | static void run_cmd_and_exit(int i, int argc, char **argv) { |
239 | EUID_ASSERT(); | 275 | EUID_ASSERT(); |
@@ -248,32 +284,36 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
248 | } | 284 | } |
249 | else if (strcmp(argv[i], "--version") == 0) { | 285 | else if (strcmp(argv[i], "--version") == 0) { |
250 | printf("firejail version %s\n", VERSION); | 286 | printf("firejail version %s\n", VERSION); |
251 | #ifndef HAVE_NETWORK | 287 | printf("\n"); |
252 | printf("Networking support is disabled.\n"); | 288 | print_compiletime_support(); |
253 | #endif | 289 | printf("\n"); |
254 | #ifdef HAVE_NETWORK_RESTRICTED | 290 | exit(0); |
255 | printf("Networking support is allowed only to root user.\n"); | 291 | } |
256 | #endif | 292 | #ifdef HAVE_OVERLAYFS |
257 | #ifndef HAVE_USERNS | 293 | else if (strcmp(argv[i], "--overlay-clean") == 0) { |
258 | printf("User namespace support is disabled.\n"); | 294 | if (checkcfg(CFG_OVERLAYFS)) { |
259 | #endif | 295 | char *path; |
260 | #ifndef HAVE_SECCOMP | 296 | if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1) |
261 | printf("Seccomp-bpf support is disabled.\n"); | 297 | errExit("asprintf"); |
262 | #endif | 298 | EUID_ROOT(); |
263 | #ifndef HAVE_BIND | 299 | if (setreuid(0, 0) < 0) |
264 | printf("Bind support is disabled.\n"); | 300 | errExit("setreuid"); |
265 | #endif | 301 | if (setregid(0, 0) < 0) |
266 | #ifndef HAVE_CHROOT | 302 | errExit("setregid"); |
267 | printf("Chroot support is disabled.\n"); | 303 | errno = 0; |
268 | #endif | 304 | int rv = remove_directory(path); |
269 | #ifndef HAVE_X11 | 305 | if (rv) { |
270 | printf("X11 support is disabled.\n"); | 306 | fprintf(stderr, "Error: cannot removed overlays stored in ~/.firejail directory, errno %d\n", errno); |
271 | #endif | 307 | exit(1); |
272 | #ifndef HAVE_FILE_TRANSFER | 308 | } |
273 | printf("File transfer support is disabled.\n"); | 309 | } |
274 | #endif | 310 | else { |
311 | fprintf(stderr, "Error: overlayfs feature is disabled in Firejail configuration file\n"); | ||
312 | exit(1); | ||
313 | } | ||
275 | exit(0); | 314 | exit(0); |
276 | } | 315 | } |
316 | #endif | ||
277 | #ifdef HAVE_X11 | 317 | #ifdef HAVE_X11 |
278 | else if (strcmp(argv[i], "--x11") == 0) { | 318 | else if (strcmp(argv[i], "--x11") == 0) { |
279 | if (checkcfg(CFG_X11)) { | 319 | if (checkcfg(CFG_X11)) { |
@@ -361,11 +401,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
361 | } | 401 | } |
362 | 402 | ||
363 | // extract pid or sandbox name | 403 | // extract pid or sandbox name |
364 | pid_t pid; | 404 | pid_t pid = read_pid(argv[i] + 12); |
365 | if (read_pid(argv[i] + 12, &pid) == 0) | 405 | bandwidth_pid(pid, cmd, dev, down, up); |
366 | bandwidth_pid(pid, cmd, dev, down, up); | ||
367 | else | ||
368 | bandwidth_name(argv[i] + 12, cmd, dev, down, up); | ||
369 | } | 406 | } |
370 | else { | 407 | else { |
371 | fprintf(stderr, "Error: networking features are disabled in Firejail configuration file\n"); | 408 | fprintf(stderr, "Error: networking features are disabled in Firejail configuration file\n"); |
@@ -380,8 +417,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
380 | #ifdef HAVE_SECCOMP | 417 | #ifdef HAVE_SECCOMP |
381 | else if (strcmp(argv[i], "--debug-syscalls") == 0) { | 418 | else if (strcmp(argv[i], "--debug-syscalls") == 0) { |
382 | if (checkcfg(CFG_SECCOMP)) { | 419 | if (checkcfg(CFG_SECCOMP)) { |
383 | syscall_print(); | 420 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-syscalls"); |
384 | exit(0); | 421 | exit(rv); |
385 | } | 422 | } |
386 | else { | 423 | else { |
387 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 424 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -390,7 +427,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
390 | } | 427 | } |
391 | else if (strcmp(argv[i], "--debug-errnos") == 0) { | 428 | else if (strcmp(argv[i], "--debug-errnos") == 0) { |
392 | if (checkcfg(CFG_SECCOMP)) { | 429 | if (checkcfg(CFG_SECCOMP)) { |
393 | errno_print(); | 430 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-errnos"); |
431 | exit(rv); | ||
394 | } | 432 | } |
395 | else { | 433 | else { |
396 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 434 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -401,11 +439,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
401 | else if (strncmp(argv[i], "--seccomp.print=", 16) == 0) { | 439 | else if (strncmp(argv[i], "--seccomp.print=", 16) == 0) { |
402 | if (checkcfg(CFG_SECCOMP)) { | 440 | if (checkcfg(CFG_SECCOMP)) { |
403 | // print seccomp filter for a sandbox specified by pid or by name | 441 | // print seccomp filter for a sandbox specified by pid or by name |
404 | pid_t pid; | 442 | pid_t pid = read_pid(argv[i] + 16); |
405 | if (read_pid(argv[i] + 16, &pid) == 0) | 443 | seccomp_print_filter(pid); |
406 | seccomp_print_filter(pid); | ||
407 | else | ||
408 | seccomp_print_filter_name(argv[i] + 16); | ||
409 | } | 444 | } |
410 | else { | 445 | else { |
411 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 446 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -414,17 +449,14 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
414 | exit(0); | 449 | exit(0); |
415 | } | 450 | } |
416 | else if (strcmp(argv[i], "--debug-protocols") == 0) { | 451 | else if (strcmp(argv[i], "--debug-protocols") == 0) { |
417 | protocol_list(); | 452 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-protocols"); |
418 | exit(0); | 453 | exit(rv); |
419 | } | 454 | } |
420 | else if (strncmp(argv[i], "--protocol.print=", 17) == 0) { | 455 | else if (strncmp(argv[i], "--protocol.print=", 17) == 0) { |
421 | if (checkcfg(CFG_SECCOMP)) { | 456 | if (checkcfg(CFG_SECCOMP)) { |
422 | // print seccomp filter for a sandbox specified by pid or by name | 457 | // print seccomp filter for a sandbox specified by pid or by name |
423 | pid_t pid; | 458 | pid_t pid = read_pid(argv[i] + 17); |
424 | if (read_pid(argv[i] + 17, &pid) == 0) | 459 | protocol_print_filter(pid); |
425 | protocol_print_filter(pid); | ||
426 | else | ||
427 | protocol_print_filter_name(argv[i] + 17); | ||
428 | } | 460 | } |
429 | else { | 461 | else { |
430 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 462 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -435,38 +467,26 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
435 | #endif | 467 | #endif |
436 | else if (strncmp(argv[i], "--cpu.print=", 12) == 0) { | 468 | else if (strncmp(argv[i], "--cpu.print=", 12) == 0) { |
437 | // join sandbox by pid or by name | 469 | // join sandbox by pid or by name |
438 | pid_t pid; | 470 | pid_t pid = read_pid(argv[i] + 12); |
439 | if (read_pid(argv[i] + 12, &pid) == 0) | 471 | cpu_print_filter(pid); |
440 | cpu_print_filter(pid); | ||
441 | else | ||
442 | cpu_print_filter_name(argv[i] + 12); | ||
443 | exit(0); | 472 | exit(0); |
444 | } | 473 | } |
445 | else if (strncmp(argv[i], "--caps.print=", 13) == 0) { | 474 | else if (strncmp(argv[i], "--caps.print=", 13) == 0) { |
446 | // join sandbox by pid or by name | 475 | // join sandbox by pid or by name |
447 | pid_t pid; | 476 | pid_t pid = read_pid(argv[i] + 13); |
448 | if (read_pid(argv[i] + 13, &pid) == 0) | 477 | caps_print_filter(pid); |
449 | caps_print_filter(pid); | ||
450 | else | ||
451 | caps_print_filter_name(argv[i] + 13); | ||
452 | exit(0); | 478 | exit(0); |
453 | } | 479 | } |
454 | else if (strncmp(argv[i], "--fs.print=", 11) == 0) { | 480 | else if (strncmp(argv[i], "--fs.print=", 11) == 0) { |
455 | // join sandbox by pid or by name | 481 | // join sandbox by pid or by name |
456 | pid_t pid; | 482 | pid_t pid = read_pid(argv[i] + 11); |
457 | if (read_pid(argv[i] + 11, &pid) == 0) | 483 | fs_logger_print_log(pid); |
458 | fs_logger_print_log(pid); | ||
459 | else | ||
460 | fs_logger_print_log_name(argv[i] + 11); | ||
461 | exit(0); | 484 | exit(0); |
462 | } | 485 | } |
463 | else if (strncmp(argv[i], "--dns.print=", 12) == 0) { | 486 | else if (strncmp(argv[i], "--dns.print=", 12) == 0) { |
464 | // join sandbox by pid or by name | 487 | // join sandbox by pid or by name |
465 | pid_t pid; | 488 | pid_t pid = read_pid(argv[i] + 12); |
466 | if (read_pid(argv[i] + 12, &pid) == 0) | 489 | net_dns_print(pid); |
467 | net_dns_print(pid); | ||
468 | else | ||
469 | net_dns_print_name(argv[i] + 12); | ||
470 | exit(0); | 490 | exit(0); |
471 | } | 491 | } |
472 | else if (strcmp(argv[i], "--debug-caps") == 0) { | 492 | else if (strcmp(argv[i], "--debug-caps") == 0) { |
@@ -474,27 +494,44 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
474 | exit(0); | 494 | exit(0); |
475 | } | 495 | } |
476 | else if (strcmp(argv[i], "--list") == 0) { | 496 | else if (strcmp(argv[i], "--list") == 0) { |
477 | list(); | 497 | if (pid_hidepid()) |
498 | sbox_run(SBOX_ROOT| SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--list"); | ||
499 | else | ||
500 | sbox_run(SBOX_USER| SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--list"); | ||
478 | exit(0); | 501 | exit(0); |
479 | } | 502 | } |
480 | else if (strcmp(argv[i], "--tree") == 0) { | 503 | else if (strcmp(argv[i], "--tree") == 0) { |
481 | tree(); | 504 | if (pid_hidepid()) |
505 | sbox_run(SBOX_ROOT | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--tree"); | ||
506 | else | ||
507 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--tree"); | ||
482 | exit(0); | 508 | exit(0); |
483 | } | 509 | } |
484 | else if (strcmp(argv[i], "--top") == 0) { | 510 | else if (strcmp(argv[i], "--top") == 0) { |
485 | top(); | 511 | if (pid_hidepid()) |
512 | sbox_run(SBOX_ROOT | SBOX_CAPS_NONE | SBOX_SECCOMP | SBOX_ALLOW_STDIN, | ||
513 | 2, PATH_FIREMON, "--top"); | ||
514 | else | ||
515 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP | SBOX_ALLOW_STDIN, | ||
516 | 2, PATH_FIREMON, "--top"); | ||
486 | exit(0); | 517 | exit(0); |
487 | } | 518 | } |
488 | #ifdef HAVE_NETWORK | 519 | #ifdef HAVE_NETWORK |
489 | else if (strcmp(argv[i], "--netstats") == 0) { | 520 | else if (strcmp(argv[i], "--netstats") == 0) { |
490 | if (checkcfg(CFG_NETWORK)) { | 521 | if (checkcfg(CFG_NETWORK)) { |
491 | netstats(); | 522 | struct stat s; |
523 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0 || pid_hidepid()) | ||
524 | sbox_run(SBOX_ROOT | SBOX_CAPS_NONE | SBOX_SECCOMP | SBOX_ALLOW_STDIN, | ||
525 | 2, PATH_FIREMON, "--netstats"); | ||
526 | else | ||
527 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP | SBOX_ALLOW_STDIN, | ||
528 | 2, PATH_FIREMON, "--netstats"); | ||
529 | exit(0); | ||
492 | } | 530 | } |
493 | else { | 531 | else { |
494 | fprintf(stderr, "Error: networking features are disabled in Firejail configuration file\n"); | 532 | fprintf(stderr, "Error: networking features are disabled in Firejail configuration file\n"); |
495 | exit(1); | 533 | exit(1); |
496 | } | 534 | } |
497 | exit(0); | ||
498 | } | 535 | } |
499 | #endif | 536 | #endif |
500 | #ifdef HAVE_FILE_TRANSFER | 537 | #ifdef HAVE_FILE_TRANSFER |
@@ -515,11 +552,40 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
515 | } | 552 | } |
516 | 553 | ||
517 | // get file | 554 | // get file |
518 | pid_t pid; | 555 | pid_t pid = read_pid(argv[i] + 6); |
519 | if (read_pid(argv[i] + 6, &pid) == 0) | 556 | sandboxfs(SANDBOX_FS_GET, pid, path, NULL); |
520 | sandboxfs(SANDBOX_FS_GET, pid, path); | 557 | exit(0); |
521 | else | 558 | } |
522 | sandboxfs_name(SANDBOX_FS_GET, argv[i] + 6, path); | 559 | else { |
560 | fprintf(stderr, "Error: --get feature is disabled in Firejail configuration file\n"); | ||
561 | exit(1); | ||
562 | } | ||
563 | } | ||
564 | else if (strncmp(argv[i], "--put=", 6) == 0) { | ||
565 | if (checkcfg(CFG_FILE_TRANSFER)) { | ||
566 | logargs(argc, argv); | ||
567 | |||
568 | // verify path | ||
569 | if ((i + 3) != argc) { | ||
570 | fprintf(stderr, "Error: invalid --put option, 2 paths expected\n"); | ||
571 | exit(1); | ||
572 | } | ||
573 | char *path1 = argv[i + 1]; | ||
574 | invalid_filename(path1); | ||
575 | if (strstr(path1, "..")) { | ||
576 | fprintf(stderr, "Error: invalid file name %s\n", path1); | ||
577 | exit(1); | ||
578 | } | ||
579 | char *path2 = argv[i + 2]; | ||
580 | invalid_filename(path2); | ||
581 | if (strstr(path2, "..")) { | ||
582 | fprintf(stderr, "Error: invalid file name %s\n", path2); | ||
583 | exit(1); | ||
584 | } | ||
585 | |||
586 | // get file | ||
587 | pid_t pid = read_pid(argv[i] + 6); | ||
588 | sandboxfs(SANDBOX_FS_PUT, pid, path1, path2); | ||
523 | exit(0); | 589 | exit(0); |
524 | } | 590 | } |
525 | else { | 591 | else { |
@@ -544,11 +610,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
544 | } | 610 | } |
545 | 611 | ||
546 | // list directory contents | 612 | // list directory contents |
547 | pid_t pid; | 613 | pid_t pid = read_pid(argv[i] + 5); |
548 | if (read_pid(argv[i] + 5, &pid) == 0) | 614 | sandboxfs(SANDBOX_FS_LS, pid, path, NULL); |
549 | sandboxfs(SANDBOX_FS_LS, pid, path); | ||
550 | else | ||
551 | sandboxfs_name(SANDBOX_FS_LS, argv[i] + 5, path); | ||
552 | exit(0); | 615 | exit(0); |
553 | } | 616 | } |
554 | else { | 617 | else { |
@@ -559,14 +622,49 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
559 | #endif | 622 | #endif |
560 | else if (strncmp(argv[i], "--join=", 7) == 0) { | 623 | else if (strncmp(argv[i], "--join=", 7) == 0) { |
561 | logargs(argc, argv); | 624 | logargs(argc, argv); |
562 | 625 | ||
626 | if (arg_shell_none) { | ||
627 | if (argc <= (i+1)) { | ||
628 | fprintf(stderr, "Error: --shell=none set, but no command specified\n"); | ||
629 | exit(1); | ||
630 | } | ||
631 | cfg.original_program_index = i + 1; | ||
632 | } | ||
633 | |||
634 | if (!cfg.shell && !arg_shell_none) | ||
635 | cfg.shell = guess_shell(); | ||
636 | |||
563 | // join sandbox by pid or by name | 637 | // join sandbox by pid or by name |
638 | pid_t pid = read_pid(argv[i] + 7); | ||
639 | join(pid, argc, argv, i + 1); | ||
640 | exit(0); | ||
641 | |||
642 | } | ||
643 | else if (strncmp(argv[i], "--join-or-start=", 16) == 0) { | ||
644 | // NOTE: this is first part of option handler, | ||
645 | // sandbox name is set in other part | ||
646 | logargs(argc, argv); | ||
647 | |||
648 | if (arg_shell_none) { | ||
649 | if (argc <= (i+1)) { | ||
650 | fprintf(stderr, "Error: --shell=none set, but no command specified\n"); | ||
651 | exit(1); | ||
652 | } | ||
653 | cfg.original_program_index = i + 1; | ||
654 | } | ||
655 | |||
656 | #if 0 // todo: redo it | ||
657 | // try to join by name only | ||
564 | pid_t pid; | 658 | pid_t pid; |
565 | if (read_pid(argv[i] + 7, &pid) == 0) | 659 | if (!name2pid(argv[i] + 16, &pid)) { |
660 | if (!cfg.shell && !arg_shell_none) | ||
661 | cfg.shell = guess_shell(); | ||
662 | |||
566 | join(pid, argc, argv, i + 1); | 663 | join(pid, argc, argv, i + 1); |
567 | else | 664 | exit(0); |
568 | join_name(argv[i] + 7, argc, argv, i + 1); | 665 | } |
569 | exit(0); | 666 | #endif |
667 | // if there no such sandbox continue argument processing | ||
570 | } | 668 | } |
571 | #ifdef HAVE_NETWORK | 669 | #ifdef HAVE_NETWORK |
572 | else if (strncmp(argv[i], "--join-network=", 15) == 0) { | 670 | else if (strncmp(argv[i], "--join-network=", 15) == 0) { |
@@ -578,12 +676,12 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
578 | exit(1); | 676 | exit(1); |
579 | } | 677 | } |
580 | 678 | ||
679 | if (!cfg.shell && !arg_shell_none) | ||
680 | cfg.shell = guess_shell(); | ||
681 | |||
581 | // join sandbox by pid or by name | 682 | // join sandbox by pid or by name |
582 | pid_t pid; | 683 | pid_t pid = read_pid(argv[i] + 15); |
583 | if (read_pid(argv[i] + 15, &pid) == 0) | 684 | join(pid, argc, argv, i + 1); |
584 | join(pid, argc, argv, i + 1); | ||
585 | else | ||
586 | join_name(argv[i] + 15, argc, argv, i + 1); | ||
587 | } | 685 | } |
588 | else { | 686 | else { |
589 | fprintf(stderr, "Error: networking features are disabled in Firejail configuration file\n"); | 687 | fprintf(stderr, "Error: networking features are disabled in Firejail configuration file\n"); |
@@ -601,23 +699,20 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
601 | exit(1); | 699 | exit(1); |
602 | } | 700 | } |
603 | 701 | ||
702 | if (!cfg.shell && !arg_shell_none) | ||
703 | cfg.shell = guess_shell(); | ||
704 | |||
604 | // join sandbox by pid or by name | 705 | // join sandbox by pid or by name |
605 | pid_t pid; | 706 | pid_t pid = read_pid(argv[i] + 18); |
606 | if (read_pid(argv[i] + 18, &pid) == 0) | 707 | join(pid, argc, argv, i + 1); |
607 | join(pid, argc, argv, i + 1); | ||
608 | else | ||
609 | join_name(argv[i] + 18, argc, argv, i + 1); | ||
610 | exit(0); | 708 | exit(0); |
611 | } | 709 | } |
612 | else if (strncmp(argv[i], "--shutdown=", 11) == 0) { | 710 | else if (strncmp(argv[i], "--shutdown=", 11) == 0) { |
613 | logargs(argc, argv); | 711 | logargs(argc, argv); |
614 | 712 | ||
615 | // shutdown sandbox by pid or by name | 713 | // shutdown sandbox by pid or by name |
616 | pid_t pid; | 714 | pid_t pid = read_pid(argv[i] + 11); |
617 | if (read_pid(argv[i] + 11, &pid) == 0) | 715 | shut(pid); |
618 | shut(pid); | ||
619 | else | ||
620 | shut_name(argv[i] + 11); | ||
621 | exit(0); | 716 | exit(0); |
622 | } | 717 | } |
623 | 718 | ||
@@ -635,14 +730,10 @@ static void set_name_file(pid_t pid) { | |||
635 | exit(1); | 730 | exit(1); |
636 | } | 731 | } |
637 | fprintf(fp, "%s\n", cfg.name); | 732 | fprintf(fp, "%s\n", cfg.name); |
638 | fclose(fp); | 733 | |
639 | |||
640 | // mode and ownership | 734 | // mode and ownership |
641 | if (chown(fname, 0, 0) == -1) | 735 | SET_PERMS_STREAM(fp, 0, 0, 0644); |
642 | errExit("chown"); | 736 | fclose(fp); |
643 | if (chmod(fname, 0644) == -1) | ||
644 | errExit("chmod"); | ||
645 | |||
646 | } | 737 | } |
647 | 738 | ||
648 | static void delete_name_file(pid_t pid) { | 739 | static void delete_name_file(pid_t pid) { |
@@ -651,6 +742,7 @@ static void delete_name_file(pid_t pid) { | |||
651 | errExit("asprintf"); | 742 | errExit("asprintf"); |
652 | int rv = unlink(fname); | 743 | int rv = unlink(fname); |
653 | (void) rv; | 744 | (void) rv; |
745 | free(fname); | ||
654 | } | 746 | } |
655 | 747 | ||
656 | static void set_x11_file(pid_t pid, int display) { | 748 | static void set_x11_file(pid_t pid, int display) { |
@@ -665,14 +757,10 @@ static void set_x11_file(pid_t pid, int display) { | |||
665 | exit(1); | 757 | exit(1); |
666 | } | 758 | } |
667 | fprintf(fp, "%d\n", display); | 759 | fprintf(fp, "%d\n", display); |
668 | fclose(fp); | 760 | |
669 | |||
670 | // mode and ownership | 761 | // mode and ownership |
671 | if (chown(fname, 0, 0) == -1) | 762 | SET_PERMS_STREAM(fp, 0, 0, 0644); |
672 | errExit("chown"); | 763 | fclose(fp); |
673 | if (chmod(fname, 0644) == -1) | ||
674 | errExit("chmod"); | ||
675 | |||
676 | } | 764 | } |
677 | 765 | ||
678 | static void delete_x11_file(pid_t pid) { | 766 | static void delete_x11_file(pid_t pid) { |
@@ -681,6 +769,62 @@ static void delete_x11_file(pid_t pid) { | |||
681 | errExit("asprintf"); | 769 | errExit("asprintf"); |
682 | int rv = unlink(fname); | 770 | int rv = unlink(fname); |
683 | (void) rv; | 771 | (void) rv; |
772 | free(fname); | ||
773 | } | ||
774 | |||
775 | static void detect_quiet(int argc, char **argv) { | ||
776 | int i; | ||
777 | |||
778 | // detect --quiet | ||
779 | for (i = 1; i < argc; i++) { | ||
780 | if (strcmp(argv[i], "--quiet") == 0) { | ||
781 | arg_quiet = 1; | ||
782 | break; | ||
783 | } | ||
784 | |||
785 | // detect end of firejail params | ||
786 | if (strcmp(argv[i], "--") == 0) | ||
787 | break; | ||
788 | if (strncmp(argv[i], "--", 2) != 0) | ||
789 | break; | ||
790 | } | ||
791 | } | ||
792 | |||
793 | static void detect_allow_debuggers(int argc, char **argv) { | ||
794 | int i; | ||
795 | |||
796 | // detect --allow-debuggers | ||
797 | for (i = 1; i < argc; i++) { | ||
798 | if (strcmp(argv[i], "--allow-debuggers") == 0) { | ||
799 | arg_allow_debuggers = 1; | ||
800 | break; | ||
801 | } | ||
802 | |||
803 | // detect end of firejail params | ||
804 | if (strcmp(argv[i], "--") == 0) | ||
805 | break; | ||
806 | if (strncmp(argv[i], "--", 2) != 0) | ||
807 | break; | ||
808 | } | ||
809 | } | ||
810 | |||
811 | char *guess_shell(void) { | ||
812 | char *shell = NULL; | ||
813 | // shells in order of preference | ||
814 | char *shells[] = {"/bin/bash", "/bin/csh", "/usr/bin/zsh", "/bin/sh", "/bin/ash", NULL }; | ||
815 | |||
816 | int i = 0; | ||
817 | while (shells[i] != NULL) { | ||
818 | struct stat s; | ||
819 | // access call checks as real UID/GID, not as effective UID/GID | ||
820 | if (stat(shells[i], &s) == 0 && access(shells[i], R_OK) == 0) { | ||
821 | shell = shells[i]; | ||
822 | break; | ||
823 | } | ||
824 | i++; | ||
825 | } | ||
826 | |||
827 | return shell; | ||
684 | } | 828 | } |
685 | 829 | ||
686 | //******************************************* | 830 | //******************************************* |
@@ -694,81 +838,106 @@ int main(int argc, char **argv) { | |||
694 | int option_force = 0; | 838 | int option_force = 0; |
695 | int custom_profile = 0; // custom profile loaded | 839 | int custom_profile = 0; // custom profile loaded |
696 | char *custom_profile_dir = NULL; // custom profile directory | 840 | char *custom_profile_dir = NULL; // custom profile directory |
697 | int arg_noprofile = 0; // use generic.profile if none other found/specified | 841 | int arg_noprofile = 0; // use default.profile if none other found/specified |
698 | #ifdef HAVE_SECCOMP | 842 | |
699 | int highest_errno = errno_highest_nr(); | 843 | // build /run/firejail directory structure |
700 | #endif | 844 | preproc_build_firejail_dir(); |
845 | |||
846 | detect_quiet(argc, argv); | ||
847 | detect_allow_debuggers(argc, argv); | ||
701 | 848 | ||
702 | // drop permissions by default and rise them when required | 849 | // drop permissions by default and rise them when required |
703 | EUID_INIT(); | 850 | EUID_INIT(); |
704 | EUID_USER(); | 851 | EUID_USER(); |
705 | 852 | ||
853 | |||
706 | // check argv[0] symlink wrapper if this is not a login shell | 854 | // check argv[0] symlink wrapper if this is not a login shell |
707 | if (*argv[0] != '-') | 855 | if (*argv[0] != '-') |
708 | run_symlink(argc, argv); | 856 | run_symlink(argc, argv); |
709 | 857 | ||
710 | // check if we already have a sandbox running | 858 | // check if we already have a sandbox running |
859 | // If LXC is detected, start firejail sandbox | ||
860 | // otherwise try to detect a PID namespace by looking under /proc for specific kernel processes and: | ||
861 | // - if --force flag is set, start firejail sandbox | ||
862 | // -- if --force flag is not set, start the application in a /bin/bash shell | ||
863 | if (check_namespace_virt() == 0) { | ||
864 | EUID_ROOT(); | ||
865 | int rv = check_kernel_procs(); | ||
866 | EUID_USER(); | ||
867 | if (rv == 0) { | ||
868 | // if --force option is passed to the program, disregard the existing sandbox | ||
869 | int found = 0; | ||
870 | for (i = 1; i < argc; i++) { | ||
871 | if (strcmp(argv[i], "--force") == 0 || | ||
872 | strcmp(argv[i], "--list") == 0 || | ||
873 | strcmp(argv[i], "--netstats") == 0 || | ||
874 | strcmp(argv[i], "--tree") == 0 || | ||
875 | strcmp(argv[i], "--top") == 0 || | ||
876 | strncmp(argv[i], "--ls=", 5) == 0 || | ||
877 | strncmp(argv[i], "--get=", 6) == 0 || | ||
878 | strcmp(argv[i], "--debug-caps") == 0 || | ||
879 | strcmp(argv[i], "--debug-errnos") == 0 || | ||
880 | strcmp(argv[i], "--debug-syscalls") == 0 || | ||
881 | strcmp(argv[i], "--debug-protocols") == 0 || | ||
882 | strcmp(argv[i], "--help") == 0 || | ||
883 | strcmp(argv[i], "--version") == 0 || | ||
884 | strcmp(argv[i], "--overlay-clean") == 0 || | ||
885 | strncmp(argv[i], "--dns.print=", 12) == 0 || | ||
886 | strncmp(argv[i], "--bandwidth=", 12) == 0 || | ||
887 | strncmp(argv[i], "--caps.print=", 13) == 0 || | ||
888 | strncmp(argv[i], "--cpu.print=", 12) == 0 || | ||
889 | //******************************************************************************** | ||
890 | // todo: fix the following problems | ||
891 | strncmp(argv[i], "--join=", 7) == 0 || | ||
892 | //[netblue@debian Downloads]$ firejail --join=896 | ||
893 | //Switching to pid 897, the first child process inside the sandbox | ||
894 | //Error: seccomp file not found | ||
895 | //******************************************************************************** | ||
896 | |||
897 | strncmp(argv[i], "--join-filesystem=", 18) == 0 || | ||
898 | strncmp(argv[i], "--join-network=", 15) == 0 || | ||
899 | strncmp(argv[i], "--fs.print=", 11) == 0 || | ||
900 | strncmp(argv[i], "--protocol.print=", 17) == 0 || | ||
901 | strncmp(argv[i], "--seccomp.print", 15) == 0 || | ||
902 | strncmp(argv[i], "--shutdown=", 11) == 0) { | ||
903 | found = 1; | ||
904 | break; | ||
905 | } | ||
906 | |||
907 | // detect end of firejail params | ||
908 | if (strcmp(argv[i], "--") == 0) | ||
909 | break; | ||
910 | if (strncmp(argv[i], "--", 2) != 0) | ||
911 | break; | ||
912 | } | ||
913 | |||
914 | if (found == 0) { | ||
915 | // start the program directly without sandboxing | ||
916 | run_no_sandbox(argc, argv); | ||
917 | // it will never get here! | ||
918 | assert(0); | ||
919 | } | ||
920 | else | ||
921 | option_force = 1; | ||
922 | } | ||
923 | } | ||
924 | |||
925 | // check root/suid | ||
711 | EUID_ROOT(); | 926 | EUID_ROOT(); |
712 | int rv = check_kernel_procs(); | 927 | if (geteuid()) { |
713 | EUID_USER(); | 928 | // detect --version |
714 | if (rv == 0) { | ||
715 | // if --force option is passed to the program, disregard the existing sandbox | ||
716 | int found = 0; | ||
717 | for (i = 1; i < argc; i++) { | 929 | for (i = 1; i < argc; i++) { |
718 | if (strcmp(argv[i], "--force") == 0 || | 930 | if (strcmp(argv[i], "--version") == 0) { |
719 | strcmp(argv[i], "--list") == 0 || | 931 | printf("firejail version %s\n", VERSION); |
720 | strcmp(argv[i], "--netstats") == 0 || | 932 | exit(0); |
721 | strcmp(argv[i], "--tree") == 0 || | ||
722 | strcmp(argv[i], "--top") == 0 || | ||
723 | strncmp(argv[i], "--ls=", 5) == 0 || | ||
724 | strncmp(argv[i], "--get=", 6) == 0 || | ||
725 | strcmp(argv[i], "--debug-caps") == 0 || | ||
726 | strcmp(argv[i], "--debug-errnos") == 0 || | ||
727 | strcmp(argv[i], "--debug-syscalls") == 0 || | ||
728 | strcmp(argv[i], "--debug-protocols") == 0 || | ||
729 | strcmp(argv[i], "--help") == 0 || | ||
730 | strcmp(argv[i], "--version") == 0 || | ||
731 | strncmp(argv[i], "--dns.print=", 12) == 0 || | ||
732 | strncmp(argv[i], "--bandwidth=", 12) == 0 || | ||
733 | strncmp(argv[i], "--caps.print=", 13) == 0 || | ||
734 | strncmp(argv[i], "--cpu.print=", 12) == 0 || | ||
735 | //******************************************************************************** | ||
736 | // todo: fix the following problems | ||
737 | strncmp(argv[i], "--join=", 7) == 0 || | ||
738 | //[netblue@debian Downloads]$ firejail --join=896 | ||
739 | //Switching to pid 897, the first child process inside the sandbox | ||
740 | //Error: seccomp file not found | ||
741 | //******************************************************************************** | ||
742 | |||
743 | strncmp(argv[i], "--join-filesystem=", 18) == 0 || | ||
744 | strncmp(argv[i], "--join-network=", 15) == 0 || | ||
745 | strncmp(argv[i], "--fs.print=", 11) == 0 || | ||
746 | strncmp(argv[i], "--protocol.print=", 17) == 0 || | ||
747 | strncmp(argv[i], "--seccomp.print", 15) == 0 || | ||
748 | strncmp(argv[i], "--shutdown=", 11) == 0) { | ||
749 | found = 1; | ||
750 | break; | ||
751 | } | 933 | } |
934 | |||
935 | // detect end of firejail params | ||
752 | if (strcmp(argv[i], "--") == 0) | 936 | if (strcmp(argv[i], "--") == 0) |
753 | break; | 937 | break; |
754 | if (strncmp(argv[i], "--", 2) != 0) | 938 | if (strncmp(argv[i], "--", 2) != 0) |
755 | break; | 939 | break; |
756 | } | 940 | } |
757 | |||
758 | if (found == 0) { | ||
759 | // start the program directly without sandboxing | ||
760 | run_no_sandbox(argc, argv); | ||
761 | // it will never get here! | ||
762 | assert(0); | ||
763 | } | ||
764 | else | ||
765 | option_force = 1; | ||
766 | } | ||
767 | |||
768 | // check root/suid | ||
769 | EUID_ROOT(); | ||
770 | if (geteuid()) { | ||
771 | fprintf(stderr, "Error: the sandbox is not setuid root\n"); | ||
772 | exit(1); | 941 | exit(1); |
773 | } | 942 | } |
774 | EUID_USER(); | 943 | EUID_USER(); |
@@ -776,10 +945,8 @@ int main(int argc, char **argv) { | |||
776 | // initialize globals | 945 | // initialize globals |
777 | init_cfg(argc, argv); | 946 | init_cfg(argc, argv); |
778 | 947 | ||
779 | |||
780 | // check firejail directories | 948 | // check firejail directories |
781 | EUID_ROOT(); | 949 | EUID_ROOT(); |
782 | fs_build_firejail_dir(); | ||
783 | bandwidth_del_run_file(sandbox_pid); | 950 | bandwidth_del_run_file(sandbox_pid); |
784 | network_del_run_file(sandbox_pid); | 951 | network_del_run_file(sandbox_pid); |
785 | delete_name_file(sandbox_pid); | 952 | delete_name_file(sandbox_pid); |
@@ -798,15 +965,68 @@ int main(int argc, char **argv) { | |||
798 | if (strcmp(comm, "sshd") == 0) { | 965 | if (strcmp(comm, "sshd") == 0) { |
799 | arg_quiet = 1; | 966 | arg_quiet = 1; |
800 | parent_sshd = 1; | 967 | parent_sshd = 1; |
968 | |||
969 | #ifdef DEBUG_RESTRICTED_SHELL | ||
970 | {EUID_ROOT(); | ||
971 | FILE *fp = fopen("/firelog", "w"); | ||
972 | if (fp) { | ||
973 | int i; | ||
974 | fprintf(fp, "argc %d: ", argc); | ||
975 | for (i = 0; i < argc; i++) | ||
976 | fprintf(fp, "#%s# ", argv[i]); | ||
977 | fprintf(fp, "\n"); | ||
978 | fclose(fp); | ||
979 | } | ||
980 | EUID_USER();} | ||
981 | #endif | ||
982 | // run sftp and scp directly without any sandboxing | ||
983 | // regular login has argv[0] == "-firejail" | ||
984 | if (*argv[0] != '-') { | ||
985 | if (strcmp(argv[1], "-c") == 0 && argc > 2) { | ||
986 | if (strcmp(argv[2], "/usr/lib/openssh/sftp-server") == 0 || | ||
987 | strncmp(argv[2], "scp ", 4) == 0) { | ||
988 | #ifdef DEBUG_RESTRICTED_SHELL | ||
989 | {EUID_ROOT(); | ||
990 | FILE *fp = fopen("/firelog", "a"); | ||
991 | if (fp) { | ||
992 | fprintf(fp, "run without a sandbox\n"); | ||
993 | fclose(fp); | ||
994 | } | ||
995 | EUID_USER();} | ||
996 | #endif | ||
997 | |||
998 | drop_privs(1); | ||
999 | int rv = system(argv[2]); | ||
1000 | exit(rv); | ||
1001 | } | ||
1002 | } | ||
1003 | } | ||
801 | } | 1004 | } |
802 | free(comm); | 1005 | free(comm); |
803 | } | 1006 | } |
804 | } | 1007 | } |
805 | 1008 | ||
806 | // is this a login shell, or a command passed by sshd insert command line options from /etc/firejail/login.users | 1009 | // is this a login shell, or a command passed by sshd, insert command line options from /etc/firejail/login.users |
807 | if (*argv[0] == '-' || parent_sshd) { | 1010 | if (*argv[0] == '-' || parent_sshd) { |
1011 | if (argc == 1) | ||
1012 | login_shell = 1; | ||
808 | fullargc = restricted_shell(cfg.username); | 1013 | fullargc = restricted_shell(cfg.username); |
809 | if (fullargc) { | 1014 | if (fullargc) { |
1015 | |||
1016 | #ifdef DEBUG_RESTRICTED_SHELL | ||
1017 | {EUID_ROOT(); | ||
1018 | FILE *fp = fopen("/firelog", "a"); | ||
1019 | if (fp) { | ||
1020 | fprintf(fp, "fullargc %d: ", fullargc); | ||
1021 | int i; | ||
1022 | for (i = 0; i < fullargc; i++) | ||
1023 | fprintf(fp, "#%s# ", fullargv[i]); | ||
1024 | fprintf(fp, "\n"); | ||
1025 | fclose(fp); | ||
1026 | } | ||
1027 | EUID_USER();} | ||
1028 | #endif | ||
1029 | |||
810 | int j; | 1030 | int j; |
811 | for (i = 1, j = fullargc; i < argc && j < MAX_ARGS; i++, j++, fullargc++) | 1031 | for (i = 1, j = fullargc; i < argc && j < MAX_ARGS; i++, j++, fullargc++) |
812 | fullargv[j] = argv[i]; | 1032 | fullargv[j] = argv[i]; |
@@ -814,12 +1034,37 @@ int main(int argc, char **argv) { | |||
814 | // replace argc/argv with fullargc/fullargv | 1034 | // replace argc/argv with fullargc/fullargv |
815 | argv = fullargv; | 1035 | argv = fullargv; |
816 | argc = j; | 1036 | argc = j; |
1037 | |||
1038 | #ifdef DEBUG_RESTRICTED_SHELL | ||
1039 | {EUID_ROOT(); | ||
1040 | FILE *fp = fopen("/firelog", "a"); | ||
1041 | if (fp) { | ||
1042 | fprintf(fp, "argc %d: ", argc); | ||
1043 | int i; | ||
1044 | for (i = 0; i < argc; i++) | ||
1045 | fprintf(fp, "#%s# ", argv[i]); | ||
1046 | fprintf(fp, "\n"); | ||
1047 | fclose(fp); | ||
1048 | } | ||
1049 | EUID_USER();} | ||
1050 | #endif | ||
817 | } | 1051 | } |
818 | } | 1052 | } |
819 | else { | 1053 | else { |
820 | // check --output option and execute it; | 1054 | // check --output option and execute it; |
821 | check_output(argc, argv); // the function will not return if --output option was found | 1055 | check_output(argc, argv); // the function will not return if --output option was found |
822 | check_user(argc, argv); // the function will not return if --user option was found | 1056 | } |
1057 | |||
1058 | |||
1059 | // check for force-nonewprivs in /etc/firejail/firejail.config file | ||
1060 | if (checkcfg(CFG_FORCE_NONEWPRIVS)) | ||
1061 | arg_nonewprivs = 1; | ||
1062 | |||
1063 | if (arg_allow_debuggers) { | ||
1064 | char *cmd = strdup("noblacklist ${PATH}/strace"); | ||
1065 | if (!cmd) | ||
1066 | errExit("strdup"); | ||
1067 | profile_add(cmd); | ||
823 | } | 1068 | } |
824 | 1069 | ||
825 | // parse arguments | 1070 | // parse arguments |
@@ -845,14 +1090,30 @@ int main(int argc, char **argv) { | |||
845 | } | 1090 | } |
846 | else if (strcmp(argv[i], "--force") == 0) | 1091 | else if (strcmp(argv[i], "--force") == 0) |
847 | ; | 1092 | ; |
1093 | else if (strcmp(argv[i], "--allow-debuggers") == 0) { | ||
1094 | // already handled | ||
1095 | } | ||
848 | 1096 | ||
849 | //************************************* | 1097 | //************************************* |
850 | // filtering | 1098 | // filtering |
851 | //************************************* | 1099 | //************************************* |
1100 | #ifdef HAVE_APPARMOR | ||
1101 | else if (strcmp(argv[i], "--apparmor") == 0) | ||
1102 | arg_apparmor = 1; | ||
1103 | #endif | ||
852 | #ifdef HAVE_SECCOMP | 1104 | #ifdef HAVE_SECCOMP |
853 | else if (strncmp(argv[i], "--protocol=", 11) == 0) { | 1105 | else if (strncmp(argv[i], "--protocol=", 11) == 0) { |
854 | if (checkcfg(CFG_SECCOMP)) { | 1106 | if (checkcfg(CFG_SECCOMP)) { |
855 | protocol_store(argv[i] + 11); | 1107 | if (cfg.protocol) { |
1108 | if (!arg_quiet) | ||
1109 | fprintf(stderr, "Warning: a protocol list is present, the new list \"%s\" will not be installed\n", argv[i] + 11); | ||
1110 | } | ||
1111 | else { | ||
1112 | // store list | ||
1113 | cfg.protocol = strdup(argv[i] + 11); | ||
1114 | if (!cfg.protocol) | ||
1115 | errExit("strdup"); | ||
1116 | } | ||
856 | } | 1117 | } |
857 | else { | 1118 | else { |
858 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 1119 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -879,9 +1140,7 @@ int main(int argc, char **argv) { | |||
879 | exit(1); | 1140 | exit(1); |
880 | } | 1141 | } |
881 | arg_seccomp = 1; | 1142 | arg_seccomp = 1; |
882 | cfg.seccomp_list = strdup(argv[i] + 10); | 1143 | cfg.seccomp_list = seccomp_check_list(argv[i] + 10); |
883 | if (!cfg.seccomp_list) | ||
884 | errExit("strdup"); | ||
885 | } | 1144 | } |
886 | else { | 1145 | else { |
887 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 1146 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -895,9 +1154,7 @@ int main(int argc, char **argv) { | |||
895 | exit(1); | 1154 | exit(1); |
896 | } | 1155 | } |
897 | arg_seccomp = 1; | 1156 | arg_seccomp = 1; |
898 | cfg.seccomp_list_drop = strdup(argv[i] + 15); | 1157 | cfg.seccomp_list_drop = seccomp_check_list(argv[i] + 15); |
899 | if (!cfg.seccomp_list_drop) | ||
900 | errExit("strdup"); | ||
901 | } | 1158 | } |
902 | else { | 1159 | else { |
903 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 1160 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -911,43 +1168,7 @@ int main(int argc, char **argv) { | |||
911 | exit(1); | 1168 | exit(1); |
912 | } | 1169 | } |
913 | arg_seccomp = 1; | 1170 | arg_seccomp = 1; |
914 | cfg.seccomp_list_keep = strdup(argv[i] + 15); | 1171 | cfg.seccomp_list_keep = seccomp_check_list(argv[i] + 15); |
915 | if (!cfg.seccomp_list_keep) | ||
916 | errExit("strdup"); | ||
917 | } | ||
918 | else { | ||
919 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | ||
920 | exit(1); | ||
921 | } | ||
922 | } | ||
923 | else if (strncmp(argv[i], "--seccomp.e", 11) == 0 && strchr(argv[i], '=')) { | ||
924 | if (checkcfg(CFG_SECCOMP)) { | ||
925 | if (arg_seccomp && !cfg.seccomp_list_errno) { | ||
926 | fprintf(stderr, "Error: seccomp already enabled\n"); | ||
927 | exit(1); | ||
928 | } | ||
929 | char *eq = strchr(argv[i], '='); | ||
930 | char *errnoname = strndup(argv[i] + 10, eq - (argv[i] + 10)); | ||
931 | int nr = errno_find_name(errnoname); | ||
932 | if (nr == -1) { | ||
933 | fprintf(stderr, "Error: unknown errno %s\n", errnoname); | ||
934 | free(errnoname); | ||
935 | exit(1); | ||
936 | } | ||
937 | |||
938 | if (!cfg.seccomp_list_errno) | ||
939 | cfg.seccomp_list_errno = calloc(highest_errno+1, sizeof(cfg.seccomp_list_errno[0])); | ||
940 | |||
941 | if (cfg.seccomp_list_errno[nr]) { | ||
942 | fprintf(stderr, "Error: errno %s already configured\n", errnoname); | ||
943 | free(errnoname); | ||
944 | exit(1); | ||
945 | } | ||
946 | arg_seccomp = 1; | ||
947 | cfg.seccomp_list_errno[nr] = strdup(eq+1); | ||
948 | if (!cfg.seccomp_list_errno[nr]) | ||
949 | errExit("strdup"); | ||
950 | free(errnoname); | ||
951 | } | 1172 | } |
952 | else { | 1173 | else { |
953 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 1174 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -1021,6 +1242,8 @@ int main(int argc, char **argv) { | |||
1021 | read_cpu_list(argv[i] + 6); | 1242 | read_cpu_list(argv[i] + 6); |
1022 | else if (strncmp(argv[i], "--nice=", 7) == 0) { | 1243 | else if (strncmp(argv[i], "--nice=", 7) == 0) { |
1023 | cfg.nice = atoi(argv[i] + 7); | 1244 | cfg.nice = atoi(argv[i] + 7); |
1245 | if (getuid() != 0 &&cfg.nice < 0) | ||
1246 | cfg.nice = 0; | ||
1024 | arg_nice = 1; | 1247 | arg_nice = 1; |
1025 | } | 1248 | } |
1026 | else if (strncmp(argv[i], "--cgroup=", 9) == 0) { | 1249 | else if (strncmp(argv[i], "--cgroup=", 9) == 0) { |
@@ -1039,6 +1262,8 @@ int main(int argc, char **argv) { | |||
1039 | //************************************* | 1262 | //************************************* |
1040 | // filesystem | 1263 | // filesystem |
1041 | //************************************* | 1264 | //************************************* |
1265 | else if (strcmp(argv[i], "--allusers") == 0) | ||
1266 | arg_allusers = 1; | ||
1042 | #ifdef HAVE_BIND | 1267 | #ifdef HAVE_BIND |
1043 | else if (strncmp(argv[i], "--bind=", 7) == 0) { | 1268 | else if (strncmp(argv[i], "--bind=", 7) == 0) { |
1044 | if (checkcfg(CFG_BIND)) { | 1269 | if (checkcfg(CFG_BIND)) { |
@@ -1079,98 +1304,155 @@ int main(int argc, char **argv) { | |||
1079 | profile_check_line(line, 0, NULL); // will exit if something wrong | 1304 | profile_check_line(line, 0, NULL); // will exit if something wrong |
1080 | profile_add(line); | 1305 | profile_add(line); |
1081 | } | 1306 | } |
1307 | |||
1308 | #ifdef HAVE_WHITELIST | ||
1082 | else if (strncmp(argv[i], "--whitelist=", 12) == 0) { | 1309 | else if (strncmp(argv[i], "--whitelist=", 12) == 0) { |
1310 | if (checkcfg(CFG_WHITELIST)) { | ||
1311 | char *line; | ||
1312 | if (asprintf(&line, "whitelist %s", argv[i] + 12) == -1) | ||
1313 | errExit("asprintf"); | ||
1314 | |||
1315 | profile_check_line(line, 0, NULL); // will exit if something wrong | ||
1316 | profile_add(line); | ||
1317 | } | ||
1318 | else { | ||
1319 | fprintf(stderr, "Error: whitelist feature is disabled in Firejail configuration file\n"); | ||
1320 | exit(1); | ||
1321 | } | ||
1322 | } | ||
1323 | #endif | ||
1324 | |||
1325 | else if (strncmp(argv[i], "--read-only=", 12) == 0) { | ||
1083 | char *line; | 1326 | char *line; |
1084 | if (asprintf(&line, "whitelist %s", argv[i] + 12) == -1) | 1327 | if (asprintf(&line, "read-only %s", argv[i] + 12) == -1) |
1085 | errExit("asprintf"); | 1328 | errExit("asprintf"); |
1086 | 1329 | ||
1087 | profile_check_line(line, 0, NULL); // will exit if something wrong | 1330 | profile_check_line(line, 0, NULL); // will exit if something wrong |
1088 | profile_add(line); | 1331 | profile_add(line); |
1089 | } | 1332 | } |
1090 | else if (strncmp(argv[i], "--read-only=", 12) == 0) { | 1333 | else if (strncmp(argv[i], "--noexec=", 9) == 0) { |
1091 | char *line; | 1334 | char *line; |
1092 | if (asprintf(&line, "read-only %s", argv[i] + 12) == -1) | 1335 | if (asprintf(&line, "noexec %s", argv[i] + 9) == -1) |
1093 | errExit("asprintf"); | 1336 | errExit("asprintf"); |
1094 | 1337 | ||
1095 | profile_check_line(line, 0, NULL); // will exit if something wrong | 1338 | profile_check_line(line, 0, NULL); // will exit if something wrong |
1096 | profile_add(line); | 1339 | profile_add(line); |
1097 | } | 1340 | } |
1098 | else if (strcmp(argv[i], "--overlay") == 0) { | 1341 | else if (strncmp(argv[i], "--read-write=", 13) == 0) { |
1099 | if (cfg.chrootdir) { | 1342 | char *line; |
1100 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); | 1343 | if (asprintf(&line, "read-write %s", argv[i] + 13) == -1) |
1101 | exit(1); | ||
1102 | } | ||
1103 | struct stat s; | ||
1104 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
1105 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | ||
1106 | exit(1); | ||
1107 | } | ||
1108 | arg_overlay = 1; | ||
1109 | arg_overlay_keep = 1; | ||
1110 | |||
1111 | // create ~/.firejail directory | ||
1112 | char *dirname; | ||
1113 | if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1) | ||
1114 | errExit("asprintf"); | 1344 | errExit("asprintf"); |
1115 | if (stat(dirname, &s) == -1) { | 1345 | |
1116 | /* coverity[toctou] */ | 1346 | profile_check_line(line, 0, NULL); // will exit if something wrong |
1117 | if (mkdir(dirname, 0700)) | 1347 | profile_add(line); |
1118 | errExit("mkdir"); | 1348 | } |
1119 | if (chown(dirname, getuid(), getgid()) < 0) | 1349 | #ifdef HAVE_OVERLAYFS |
1120 | errExit("chown"); | 1350 | else if (strcmp(argv[i], "--overlay") == 0) { |
1121 | if (chmod(dirname, 0700) < 0) | 1351 | if (checkcfg(CFG_OVERLAYFS)) { |
1122 | errExit("chmod"); | 1352 | if (cfg.chrootdir) { |
1353 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); | ||
1354 | exit(1); | ||
1355 | } | ||
1356 | struct stat s; | ||
1357 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
1358 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | ||
1359 | exit(1); | ||
1360 | } | ||
1361 | arg_overlay = 1; | ||
1362 | arg_overlay_keep = 1; | ||
1363 | |||
1364 | char *subdirname; | ||
1365 | if (asprintf(&subdirname, "%d", getpid()) == -1) | ||
1366 | errExit("asprintf"); | ||
1367 | cfg.overlay_dir = fs_check_overlay_dir(subdirname, arg_overlay_reuse); | ||
1368 | |||
1369 | free(subdirname); | ||
1123 | } | 1370 | } |
1124 | else if (is_link(dirname)) { | 1371 | else { |
1125 | fprintf(stderr, "Error: invalid ~/.firejail directory\n"); | 1372 | fprintf(stderr, "Error: overlayfs feature is disabled in Firejail configuration file\n"); |
1126 | exit(1); | 1373 | exit(1); |
1127 | } | 1374 | } |
1128 | 1375 | } | |
1129 | free(dirname); | 1376 | else if (strncmp(argv[i], "--overlay-named=", 16) == 0) { |
1130 | 1377 | if (checkcfg(CFG_OVERLAYFS)) { | |
1131 | // check overlay directory | 1378 | if (cfg.chrootdir) { |
1132 | if (asprintf(&dirname, "%s/.firejail/%d", cfg.homedir, getpid()) == -1) | 1379 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); |
1133 | errExit("asprintf"); | 1380 | exit(1); |
1134 | if (stat(dirname, &s) == 0) { | 1381 | } |
1135 | fprintf(stderr, "Error: overlay directory already exists: %s\n", dirname); | 1382 | struct stat s; |
1383 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
1384 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | ||
1385 | exit(1); | ||
1386 | } | ||
1387 | arg_overlay = 1; | ||
1388 | arg_overlay_keep = 1; | ||
1389 | arg_overlay_reuse = 1; | ||
1390 | |||
1391 | char *subdirname = argv[i] + 16; | ||
1392 | if (subdirname == '\0') { | ||
1393 | fprintf(stderr, "Error: invalid overlay option\n"); | ||
1394 | exit(1); | ||
1395 | } | ||
1396 | |||
1397 | // check name | ||
1398 | invalid_filename(subdirname); | ||
1399 | if (strstr(subdirname, "..") || strstr(subdirname, "/")) { | ||
1400 | fprintf(stderr, "Error: invalid overlay name\n"); | ||
1401 | exit(1); | ||
1402 | } | ||
1403 | cfg.overlay_dir = fs_check_overlay_dir(subdirname, arg_overlay_reuse); | ||
1404 | } | ||
1405 | else { | ||
1406 | fprintf(stderr, "Error: overlayfs feature is disabled in Firejail configuration file\n"); | ||
1136 | exit(1); | 1407 | exit(1); |
1137 | } | 1408 | } |
1138 | cfg.overlay_dir = dirname; | 1409 | |
1139 | } | 1410 | } |
1140 | else if (strcmp(argv[i], "--overlay-tmpfs") == 0) { | 1411 | else if (strcmp(argv[i], "--overlay-tmpfs") == 0) { |
1141 | if (cfg.chrootdir) { | 1412 | if (checkcfg(CFG_OVERLAYFS)) { |
1142 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); | 1413 | if (cfg.chrootdir) { |
1143 | exit(1); | 1414 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); |
1415 | exit(1); | ||
1416 | } | ||
1417 | struct stat s; | ||
1418 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
1419 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | ||
1420 | exit(1); | ||
1421 | } | ||
1422 | arg_overlay = 1; | ||
1144 | } | 1423 | } |
1145 | struct stat s; | 1424 | else { |
1146 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | 1425 | fprintf(stderr, "Error: overlayfs feature is disabled in Firejail configuration file\n"); |
1147 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | 1426 | exit(1); |
1148 | exit(1); | ||
1149 | } | 1427 | } |
1150 | arg_overlay = 1; | ||
1151 | } | 1428 | } |
1429 | #endif | ||
1152 | else if (strncmp(argv[i], "--profile=", 10) == 0) { | 1430 | else if (strncmp(argv[i], "--profile=", 10) == 0) { |
1153 | if (arg_noprofile) { | 1431 | if (arg_noprofile) { |
1154 | fprintf(stderr, "Error: --noprofile and --profile options are mutually exclusive\n"); | 1432 | fprintf(stderr, "Error: --noprofile and --profile options are mutually exclusive\n"); |
1155 | exit(1); | 1433 | exit(1); |
1156 | } | 1434 | } |
1157 | invalid_filename(argv[i] + 10); | 1435 | |
1436 | char *ppath = expand_home(argv[i] + 10, cfg.homedir); | ||
1437 | if (!ppath) | ||
1438 | errExit("strdup"); | ||
1439 | invalid_filename(ppath); | ||
1158 | 1440 | ||
1159 | // multiple profile files are allowed! | 1441 | // multiple profile files are allowed! |
1160 | char *ptr = argv[i] + 10; | 1442 | if (is_dir(ppath) || is_link(ppath) || strstr(ppath, "..")) { |
1161 | if (is_dir(ptr) || is_link(ptr) || strstr(ptr, "..")) { | ||
1162 | fprintf(stderr, "Error: invalid profile file\n"); | 1443 | fprintf(stderr, "Error: invalid profile file\n"); |
1163 | exit(1); | 1444 | exit(1); |
1164 | } | 1445 | } |
1165 | 1446 | ||
1166 | // access call checks as real UID/GID, not as effective UID/GID | 1447 | // access call checks as real UID/GID, not as effective UID/GID |
1167 | if (access(argv[i] + 10, R_OK)) { | 1448 | if (access(ppath, R_OK)) { |
1168 | fprintf(stderr, "Error: cannot access profile file\n"); | 1449 | fprintf(stderr, "Error: cannot access profile file\n"); |
1169 | return 1; | 1450 | return 1; |
1170 | } | 1451 | } |
1171 | 1452 | ||
1172 | profile_read(argv[i] + 10); | 1453 | profile_read(ppath); |
1173 | custom_profile = 1; | 1454 | custom_profile = 1; |
1455 | free(ppath); | ||
1174 | } | 1456 | } |
1175 | else if (strncmp(argv[i], "--profile-path=", 15) == 0) { | 1457 | else if (strncmp(argv[i], "--profile-path=", 15) == 0) { |
1176 | if (arg_noprofile) { | 1458 | if (arg_noprofile) { |
@@ -1255,6 +1537,14 @@ int main(int argc, char **argv) { | |||
1255 | return 1; | 1537 | return 1; |
1256 | } | 1538 | } |
1257 | 1539 | ||
1540 | // don't allow "--chroot=/" | ||
1541 | char *rpath = realpath(cfg.chrootdir, NULL); | ||
1542 | if (rpath == NULL || strcmp(rpath, "/") == 0) { | ||
1543 | fprintf(stderr, "Error: invalid chroot directory\n"); | ||
1544 | exit(1); | ||
1545 | } | ||
1546 | free(rpath); | ||
1547 | |||
1258 | // check chroot directory structure | 1548 | // check chroot directory structure |
1259 | if (fs_check_chroot_dir(cfg.chrootdir)) { | 1549 | if (fs_check_chroot_dir(cfg.chrootdir)) { |
1260 | fprintf(stderr, "Error: invalid chroot\n"); | 1550 | fprintf(stderr, "Error: invalid chroot\n"); |
@@ -1265,12 +1555,27 @@ int main(int argc, char **argv) { | |||
1265 | fprintf(stderr, "Error: --chroot feature is disabled in Firejail configuration file\n"); | 1555 | fprintf(stderr, "Error: --chroot feature is disabled in Firejail configuration file\n"); |
1266 | exit(1); | 1556 | exit(1); |
1267 | } | 1557 | } |
1268 | |||
1269 | } | 1558 | } |
1270 | #endif | 1559 | #endif |
1271 | else if (strcmp(argv[i], "--private") == 0) | 1560 | else if (strcmp(argv[i], "--writable-etc") == 0) { |
1561 | if (cfg.etc_private_keep) { | ||
1562 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); | ||
1563 | exit(1); | ||
1564 | } | ||
1565 | arg_writable_etc = 1; | ||
1566 | } | ||
1567 | else if (strcmp(argv[i], "--writable-var") == 0) { | ||
1568 | arg_writable_var = 1; | ||
1569 | } | ||
1570 | else if (strcmp(argv[i], "--private") == 0) { | ||
1272 | arg_private = 1; | 1571 | arg_private = 1; |
1572 | } | ||
1273 | else if (strncmp(argv[i], "--private=", 10) == 0) { | 1573 | else if (strncmp(argv[i], "--private=", 10) == 0) { |
1574 | if (cfg.home_private_keep) { | ||
1575 | fprintf(stderr, "Error: a private list of files was already defined with --private-home option.\n"); | ||
1576 | exit(1); | ||
1577 | } | ||
1578 | |||
1274 | // extract private home dirname | 1579 | // extract private home dirname |
1275 | cfg.home_private = argv[i] + 10; | 1580 | cfg.home_private = argv[i] + 10; |
1276 | if (*cfg.home_private == '\0') { | 1581 | if (*cfg.home_private == '\0') { |
@@ -1278,12 +1583,42 @@ int main(int argc, char **argv) { | |||
1278 | exit(1); | 1583 | exit(1); |
1279 | } | 1584 | } |
1280 | fs_check_private_dir(); | 1585 | fs_check_private_dir(); |
1586 | |||
1587 | // downgrade to --private if the directory is the user home directory | ||
1588 | if (strcmp(cfg.home_private, cfg.homedir) == 0) { | ||
1589 | free(cfg.home_private); | ||
1590 | cfg.home_private = NULL; | ||
1591 | } | ||
1281 | arg_private = 1; | 1592 | arg_private = 1; |
1282 | } | 1593 | } |
1594 | #ifdef HAVE_PRIVATE_HOME | ||
1595 | else if (strncmp(argv[i], "--private-home=", 15) == 0) { | ||
1596 | if (checkcfg(CFG_PRIVATE_HOME)) { | ||
1597 | if (cfg.home_private) { | ||
1598 | fprintf(stderr, "Error: a private home directory was already defined with --private option.\n"); | ||
1599 | exit(1); | ||
1600 | } | ||
1601 | |||
1602 | // extract private home dirname | ||
1603 | cfg.home_private_keep = argv[i] + 15; | ||
1604 | fs_check_home_list(); | ||
1605 | arg_private = 1; | ||
1606 | } | ||
1607 | else { | ||
1608 | fprintf(stderr, "Error: --private-home feature is disabled in Firejail configuration file\n"); | ||
1609 | exit(1); | ||
1610 | } | ||
1611 | } | ||
1612 | #endif | ||
1283 | else if (strcmp(argv[i], "--private-dev") == 0) { | 1613 | else if (strcmp(argv[i], "--private-dev") == 0) { |
1284 | arg_private_dev = 1; | 1614 | arg_private_dev = 1; |
1285 | } | 1615 | } |
1286 | else if (strncmp(argv[i], "--private-etc=", 14) == 0) { | 1616 | else if (strncmp(argv[i], "--private-etc=", 14) == 0) { |
1617 | if (arg_writable_etc) { | ||
1618 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); | ||
1619 | exit(1); | ||
1620 | } | ||
1621 | |||
1287 | // extract private etc list | 1622 | // extract private etc list |
1288 | cfg.etc_private_keep = argv[i] + 14; | 1623 | cfg.etc_private_keep = argv[i] + 14; |
1289 | if (*cfg.etc_private_keep == '\0') { | 1624 | if (*cfg.etc_private_keep == '\0') { |
@@ -1291,12 +1626,7 @@ int main(int argc, char **argv) { | |||
1291 | exit(1); | 1626 | exit(1); |
1292 | } | 1627 | } |
1293 | fs_check_etc_list(); | 1628 | fs_check_etc_list(); |
1294 | if (*cfg.etc_private_keep != '\0') | 1629 | arg_private_etc = 1; |
1295 | arg_private_etc = 1; | ||
1296 | else { | ||
1297 | arg_private_etc = 0; | ||
1298 | fprintf(stderr, "Warning: private-etc disabled, no file found\n"); | ||
1299 | } | ||
1300 | } | 1630 | } |
1301 | else if (strncmp(argv[i], "--private-bin=", 14) == 0) { | 1631 | else if (strncmp(argv[i], "--private-bin=", 14) == 0) { |
1302 | // extract private bin list | 1632 | // extract private bin list |
@@ -1341,11 +1671,18 @@ int main(int argc, char **argv) { | |||
1341 | } | 1671 | } |
1342 | } | 1672 | } |
1343 | #endif | 1673 | #endif |
1674 | else if (strcmp(argv[i], "--nonewprivs") == 0) { | ||
1675 | arg_nonewprivs = 1; | ||
1676 | } | ||
1344 | else if (strncmp(argv[i], "--env=", 6) == 0) | 1677 | else if (strncmp(argv[i], "--env=", 6) == 0) |
1345 | env_store(argv[i] + 6); | 1678 | env_store(argv[i] + 6, SETENV); |
1346 | else if (strncmp(argv[i], "--nosound", 9) == 0) { | 1679 | else if (strncmp(argv[i], "--rmenv=", 8) == 0) |
1680 | env_store(argv[i] + 8, RMENV); | ||
1681 | else if (strcmp(argv[i], "--nosound") == 0) { | ||
1347 | arg_nosound = 1; | 1682 | arg_nosound = 1; |
1348 | arg_private_dev = 1; | 1683 | } |
1684 | else if (strcmp(argv[i], "--no3d") == 0) { | ||
1685 | arg_no3d = 1; | ||
1349 | } | 1686 | } |
1350 | 1687 | ||
1351 | //************************************* | 1688 | //************************************* |
@@ -1401,7 +1738,8 @@ int main(int argc, char **argv) { | |||
1401 | errExit("strdup"); | 1738 | errExit("strdup"); |
1402 | 1739 | ||
1403 | if (net_get_if_addr(intf->dev, &intf->ip, &intf->mask, intf->mac, &intf->mtu)) { | 1740 | if (net_get_if_addr(intf->dev, &intf->ip, &intf->mask, intf->mac, &intf->mtu)) { |
1404 | fprintf(stderr, "Warning: interface %s is not configured\n", intf->dev); | 1741 | if (!arg_quiet || arg_debug) |
1742 | fprintf(stderr, "Warning: interface %s is not configured\n", intf->dev); | ||
1405 | } | 1743 | } |
1406 | intf->configured = 1; | 1744 | intf->configured = 1; |
1407 | } | 1745 | } |
@@ -1464,6 +1802,27 @@ int main(int argc, char **argv) { | |||
1464 | } | 1802 | } |
1465 | } | 1803 | } |
1466 | 1804 | ||
1805 | else if (strncmp(argv[i], "--veth-name=", 12) == 0) { | ||
1806 | if (checkcfg(CFG_NETWORK)) { | ||
1807 | Bridge *br = last_bridge_configured(); | ||
1808 | if (br == NULL) { | ||
1809 | fprintf(stderr, "Error: no network device configured\n"); | ||
1810 | exit(1); | ||
1811 | } | ||
1812 | br->veth_name = strdup(argv[i] + 12); | ||
1813 | if (br->veth_name == NULL) | ||
1814 | errExit("strdup"); | ||
1815 | if (*br->veth_name == '\0') { | ||
1816 | fprintf(stderr, "Error: no veth-name configured\n"); | ||
1817 | exit(1); | ||
1818 | } | ||
1819 | } | ||
1820 | else { | ||
1821 | fprintf(stderr, "Error: networking features are disabled in Firejail configuration file\n"); | ||
1822 | exit(1); | ||
1823 | } | ||
1824 | } | ||
1825 | |||
1467 | else if (strcmp(argv[i], "--scan") == 0) { | 1826 | else if (strcmp(argv[i], "--scan") == 0) { |
1468 | if (checkcfg(CFG_NETWORK)) { | 1827 | if (checkcfg(CFG_NETWORK)) { |
1469 | arg_scan = 1; | 1828 | arg_scan = 1; |
@@ -1522,17 +1881,17 @@ int main(int argc, char **argv) { | |||
1522 | Bridge *br = last_bridge_configured(); | 1881 | Bridge *br = last_bridge_configured(); |
1523 | if (br == NULL) { | 1882 | if (br == NULL) { |
1524 | fprintf(stderr, "Error: no network device configured\n"); | 1883 | fprintf(stderr, "Error: no network device configured\n"); |
1525 | return 1; | 1884 | exit(1); |
1526 | } | 1885 | } |
1527 | if (mac_not_zero(br->macsandbox)) { | 1886 | if (mac_not_zero(br->macsandbox)) { |
1528 | fprintf(stderr, "Error: cannot configure the MAC address twice for the same interface\n"); | 1887 | fprintf(stderr, "Error: cannot configure the MAC address twice for the same interface\n"); |
1529 | return 1; | 1888 | exit(1); |
1530 | } | 1889 | } |
1531 | 1890 | ||
1532 | // read the address | 1891 | // read the address |
1533 | if (atomac(argv[i] + 6, br->macsandbox)) { | 1892 | if (atomac(argv[i] + 6, br->macsandbox)) { |
1534 | fprintf(stderr, "Error: invalid MAC address\n"); | 1893 | fprintf(stderr, "Error: invalid MAC address\n"); |
1535 | return 1; | 1894 | exit(1); |
1536 | } | 1895 | } |
1537 | } | 1896 | } |
1538 | else { | 1897 | else { |
@@ -1546,12 +1905,12 @@ int main(int argc, char **argv) { | |||
1546 | Bridge *br = last_bridge_configured(); | 1905 | Bridge *br = last_bridge_configured(); |
1547 | if (br == NULL) { | 1906 | if (br == NULL) { |
1548 | fprintf(stderr, "Error: no network device configured\n"); | 1907 | fprintf(stderr, "Error: no network device configured\n"); |
1549 | return 1; | 1908 | exit(1); |
1550 | } | 1909 | } |
1551 | 1910 | ||
1552 | if (sscanf(argv[i] + 6, "%d", &br->mtu) != 1 || br->mtu < 576 || br->mtu > 9198) { | 1911 | if (sscanf(argv[i] + 6, "%d", &br->mtu) != 1 || br->mtu < 576 || br->mtu > 9198) { |
1553 | fprintf(stderr, "Error: invalid mtu value\n"); | 1912 | fprintf(stderr, "Error: invalid mtu value\n"); |
1554 | return 1; | 1913 | exit(1); |
1555 | } | 1914 | } |
1556 | } | 1915 | } |
1557 | else { | 1916 | else { |
@@ -1565,11 +1924,11 @@ int main(int argc, char **argv) { | |||
1565 | Bridge *br = last_bridge_configured(); | 1924 | Bridge *br = last_bridge_configured(); |
1566 | if (br == NULL) { | 1925 | if (br == NULL) { |
1567 | fprintf(stderr, "Error: no network device configured\n"); | 1926 | fprintf(stderr, "Error: no network device configured\n"); |
1568 | return 1; | 1927 | exit(1); |
1569 | } | 1928 | } |
1570 | if (br->arg_ip_none || br->ipsandbox) { | 1929 | if (br->arg_ip_none || br->ipsandbox) { |
1571 | fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); | 1930 | fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); |
1572 | return 1; | 1931 | exit(1); |
1573 | } | 1932 | } |
1574 | 1933 | ||
1575 | // configure this IP address for the last bridge defined | 1934 | // configure this IP address for the last bridge defined |
@@ -1578,7 +1937,7 @@ int main(int argc, char **argv) { | |||
1578 | else { | 1937 | else { |
1579 | if (atoip(argv[i] + 5, &br->ipsandbox)) { | 1938 | if (atoip(argv[i] + 5, &br->ipsandbox)) { |
1580 | fprintf(stderr, "Error: invalid IP address\n"); | 1939 | fprintf(stderr, "Error: invalid IP address\n"); |
1581 | return 1; | 1940 | exit(1); |
1582 | } | 1941 | } |
1583 | } | 1942 | } |
1584 | } | 1943 | } |
@@ -1593,11 +1952,11 @@ int main(int argc, char **argv) { | |||
1593 | Bridge *br = last_bridge_configured(); | 1952 | Bridge *br = last_bridge_configured(); |
1594 | if (br == NULL) { | 1953 | if (br == NULL) { |
1595 | fprintf(stderr, "Error: no network device configured\n"); | 1954 | fprintf(stderr, "Error: no network device configured\n"); |
1596 | return 1; | 1955 | exit(1); |
1597 | } | 1956 | } |
1598 | if (br->arg_ip_none || br->ip6sandbox) { | 1957 | if (br->arg_ip_none || br->ip6sandbox) { |
1599 | fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); | 1958 | fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); |
1600 | return 1; | 1959 | exit(1); |
1601 | } | 1960 | } |
1602 | 1961 | ||
1603 | // configure this IP address for the last bridge defined | 1962 | // configure this IP address for the last bridge defined |
@@ -1605,7 +1964,7 @@ int main(int argc, char **argv) { | |||
1605 | br->ip6sandbox = argv[i] + 6; | 1964 | br->ip6sandbox = argv[i] + 6; |
1606 | // if (atoip(argv[i] + 5, &br->ipsandbox)) { | 1965 | // if (atoip(argv[i] + 5, &br->ipsandbox)) { |
1607 | // fprintf(stderr, "Error: invalid IP address\n"); | 1966 | // fprintf(stderr, "Error: invalid IP address\n"); |
1608 | // return 1; | 1967 | // exit(1); |
1609 | // } | 1968 | // } |
1610 | } | 1969 | } |
1611 | else { | 1970 | else { |
@@ -1619,7 +1978,7 @@ int main(int argc, char **argv) { | |||
1619 | if (checkcfg(CFG_NETWORK)) { | 1978 | if (checkcfg(CFG_NETWORK)) { |
1620 | if (atoip(argv[i] + 12, &cfg.defaultgw)) { | 1979 | if (atoip(argv[i] + 12, &cfg.defaultgw)) { |
1621 | fprintf(stderr, "Error: invalid IP address\n"); | 1980 | fprintf(stderr, "Error: invalid IP address\n"); |
1622 | return 1; | 1981 | exit(1); |
1623 | } | 1982 | } |
1624 | } | 1983 | } |
1625 | else { | 1984 | else { |
@@ -1649,6 +2008,18 @@ int main(int argc, char **argv) { | |||
1649 | 2008 | ||
1650 | #ifdef HAVE_NETWORK | 2009 | #ifdef HAVE_NETWORK |
1651 | else if (strcmp(argv[i], "--netfilter") == 0) { | 2010 | else if (strcmp(argv[i], "--netfilter") == 0) { |
2011 | #ifdef HAVE_NETWORK_RESTRICTED | ||
2012 | // compile time restricted networking | ||
2013 | if (getuid() != 0) { | ||
2014 | fprintf(stderr, "Error: --netfilter is only allowed for root\n"); | ||
2015 | exit(1); | ||
2016 | } | ||
2017 | #endif | ||
2018 | // run time restricted networking | ||
2019 | if (checkcfg(CFG_RESTRICTED_NETWORK) && getuid() != 0) { | ||
2020 | fprintf(stderr, "Error: --netfilter is only allowed for root\n"); | ||
2021 | exit(1); | ||
2022 | } | ||
1652 | if (checkcfg(CFG_NETWORK)) { | 2023 | if (checkcfg(CFG_NETWORK)) { |
1653 | arg_netfilter = 1; | 2024 | arg_netfilter = 1; |
1654 | } | 2025 | } |
@@ -1659,6 +2030,18 @@ int main(int argc, char **argv) { | |||
1659 | } | 2030 | } |
1660 | 2031 | ||
1661 | else if (strncmp(argv[i], "--netfilter=", 12) == 0) { | 2032 | else if (strncmp(argv[i], "--netfilter=", 12) == 0) { |
2033 | #ifdef HAVE_NETWORK_RESTRICTED | ||
2034 | // compile time restricted networking | ||
2035 | if (getuid() != 0) { | ||
2036 | fprintf(stderr, "Error: --netfilter is only allowed for root\n"); | ||
2037 | exit(1); | ||
2038 | } | ||
2039 | #endif | ||
2040 | // run time restricted networking | ||
2041 | if (checkcfg(CFG_RESTRICTED_NETWORK) && getuid() != 0) { | ||
2042 | fprintf(stderr, "Error: --netfilter is only allowed for root\n"); | ||
2043 | exit(1); | ||
2044 | } | ||
1662 | if (checkcfg(CFG_NETWORK)) { | 2045 | if (checkcfg(CFG_NETWORK)) { |
1663 | arg_netfilter = 1; | 2046 | arg_netfilter = 1; |
1664 | arg_netfilter_file = argv[i] + 12; | 2047 | arg_netfilter_file = argv[i] + 12; |
@@ -1685,32 +2068,49 @@ int main(int argc, char **argv) { | |||
1685 | //************************************* | 2068 | //************************************* |
1686 | // command | 2069 | // command |
1687 | //************************************* | 2070 | //************************************* |
2071 | else if (strcmp(argv[i], "--audit") == 0) { | ||
2072 | if (asprintf(&arg_audit_prog, "%s/firejail/faudit", LIBDIR) == -1) | ||
2073 | errExit("asprintf"); | ||
2074 | arg_audit = 1; | ||
2075 | } | ||
2076 | else if (strncmp(argv[i], "--audit=", 8) == 0) { | ||
2077 | if (strlen(argv[i] + 8) == 0) { | ||
2078 | fprintf(stderr, "Error: invalid audit program\n"); | ||
2079 | exit(1); | ||
2080 | } | ||
2081 | arg_audit_prog = strdup(argv[i] + 8); | ||
2082 | if (!arg_audit_prog) | ||
2083 | errExit("strdup"); | ||
2084 | arg_audit = 1; | ||
2085 | } | ||
2086 | else if (strcmp(argv[i], "--appimage") == 0) | ||
2087 | arg_appimage = 1; | ||
1688 | else if (strcmp(argv[i], "--csh") == 0) { | 2088 | else if (strcmp(argv[i], "--csh") == 0) { |
1689 | if (arg_shell_none) { | 2089 | if (arg_shell_none) { |
1690 | 2090 | ||
1691 | fprintf(stderr, "Error: --shell=none was already specified.\n"); | 2091 | fprintf(stderr, "Error: --shell=none was already specified.\n"); |
1692 | return 1; | 2092 | return 1; |
1693 | } | 2093 | } |
1694 | if (arg_zsh || cfg.shell ) { | 2094 | if (cfg.shell) { |
1695 | fprintf(stderr, "Error: only one default user shell can be specified\n"); | 2095 | fprintf(stderr, "Error: only one default user shell can be specified\n"); |
1696 | return 1; | 2096 | return 1; |
1697 | } | 2097 | } |
1698 | arg_csh = 1; | 2098 | cfg.shell = "/bin/csh"; |
1699 | } | 2099 | } |
1700 | else if (strcmp(argv[i], "--zsh") == 0) { | 2100 | else if (strcmp(argv[i], "--zsh") == 0) { |
1701 | if (arg_shell_none) { | 2101 | if (arg_shell_none) { |
1702 | fprintf(stderr, "Error: --shell=none was already specified.\n"); | 2102 | fprintf(stderr, "Error: --shell=none was already specified.\n"); |
1703 | return 1; | 2103 | return 1; |
1704 | } | 2104 | } |
1705 | if (arg_csh || cfg.shell ) { | 2105 | if (cfg.shell) { |
1706 | fprintf(stderr, "Error: only one default user shell can be specified\n"); | 2106 | fprintf(stderr, "Error: only one default user shell can be specified\n"); |
1707 | return 1; | 2107 | return 1; |
1708 | } | 2108 | } |
1709 | arg_zsh = 1; | 2109 | cfg.shell = "/bin/zsh"; |
1710 | } | 2110 | } |
1711 | else if (strcmp(argv[i], "--shell=none") == 0) { | 2111 | else if (strcmp(argv[i], "--shell=none") == 0) { |
1712 | arg_shell_none = 1; | 2112 | arg_shell_none = 1; |
1713 | if (arg_csh || arg_zsh || cfg.shell) { | 2113 | if (cfg.shell) { |
1714 | fprintf(stderr, "Error: a shell was already specified\n"); | 2114 | fprintf(stderr, "Error: a shell was already specified\n"); |
1715 | return 1; | 2115 | return 1; |
1716 | } | 2116 | } |
@@ -1722,7 +2122,7 @@ int main(int argc, char **argv) { | |||
1722 | } | 2122 | } |
1723 | invalid_filename(argv[i] + 8); | 2123 | invalid_filename(argv[i] + 8); |
1724 | 2124 | ||
1725 | if (arg_csh || arg_zsh || cfg.shell) { | 2125 | if (cfg.shell) { |
1726 | fprintf(stderr, "Error: only one user shell can be specified\n"); | 2126 | fprintf(stderr, "Error: only one user shell can be specified\n"); |
1727 | return 1; | 2127 | return 1; |
1728 | } | 2128 | } |
@@ -1732,9 +2132,18 @@ int main(int argc, char **argv) { | |||
1732 | fprintf(stderr, "Error: invalid shell\n"); | 2132 | fprintf(stderr, "Error: invalid shell\n"); |
1733 | exit(1); | 2133 | exit(1); |
1734 | } | 2134 | } |
1735 | 2135 | ||
1736 | // access call checks as real UID/GID, not as effective UID/GID | 2136 | // access call checks as real UID/GID, not as effective UID/GID |
1737 | if (access(cfg.shell, R_OK)) { | 2137 | if(cfg.chrootdir) { |
2138 | char *shellpath; | ||
2139 | if (asprintf(&shellpath, "%s%s", cfg.chrootdir, cfg.shell) == -1) | ||
2140 | errExit("asprintf"); | ||
2141 | if (access(shellpath, R_OK)) { | ||
2142 | fprintf(stderr, "Error: cannot access shell file in chroot\n"); | ||
2143 | exit(1); | ||
2144 | } | ||
2145 | free(shellpath); | ||
2146 | } else if (access(cfg.shell, R_OK)) { | ||
1738 | fprintf(stderr, "Error: cannot access shell file\n"); | 2147 | fprintf(stderr, "Error: cannot access shell file\n"); |
1739 | exit(1); | 2148 | exit(1); |
1740 | } | 2149 | } |
@@ -1746,6 +2155,32 @@ int main(int argc, char **argv) { | |||
1746 | return 1; | 2155 | return 1; |
1747 | } | 2156 | } |
1748 | } | 2157 | } |
2158 | |||
2159 | // unlike all other x11 features, this is available always | ||
2160 | else if (strcmp(argv[i], "--x11=none") == 0) { | ||
2161 | arg_x11_block = 1; | ||
2162 | } | ||
2163 | #ifdef HAVE_X11 | ||
2164 | else if (strcmp(argv[i], "--x11=xorg") == 0) { | ||
2165 | if (checkcfg(CFG_X11)) | ||
2166 | arg_x11_xorg = 1; | ||
2167 | else { | ||
2168 | fprintf(stderr, "Error: --x11 feature is disabled in Firejail configuration file\n"); | ||
2169 | exit(1); | ||
2170 | } | ||
2171 | } | ||
2172 | #endif | ||
2173 | else if (strncmp(argv[i], "--join-or-start=", 16) == 0) { | ||
2174 | // NOTE: this is second part of option handler, | ||
2175 | // atempt to find and join sandbox is done in other one | ||
2176 | |||
2177 | // set sandbox name and start normally | ||
2178 | cfg.name = argv[i] + 16; | ||
2179 | if (strlen(cfg.name) == 0) { | ||
2180 | fprintf(stderr, "Error: please provide a name for sandbox\n"); | ||
2181 | return 1; | ||
2182 | } | ||
2183 | } | ||
1749 | else if (strcmp(argv[i], "--") == 0) { | 2184 | else if (strcmp(argv[i], "--") == 0) { |
1750 | // double dash - positional params to follow | 2185 | // double dash - positional params to follow |
1751 | arg_doubledash = 1; | 2186 | arg_doubledash = 1; |
@@ -1766,15 +2201,33 @@ int main(int argc, char **argv) { | |||
1766 | } | 2201 | } |
1767 | 2202 | ||
1768 | // we have a program name coming | 2203 | // we have a program name coming |
1769 | extract_command_name(i, argv); | 2204 | if (arg_appimage) { |
2205 | cfg.command_name = strdup(argv[i]); | ||
2206 | if (!cfg.command_name) | ||
2207 | errExit("strdup"); | ||
2208 | } | ||
2209 | else | ||
2210 | extract_command_name(i, argv); | ||
1770 | prog_index = i; | 2211 | prog_index = i; |
1771 | break; | 2212 | break; |
1772 | } | 2213 | } |
1773 | } | 2214 | } |
2215 | |||
2216 | // prog_index could still be -1 if no program was specified | ||
2217 | if (prog_index == -1 && arg_shell_none) { | ||
2218 | fprintf(stderr, "shell=none configured, but no program specified\n"); | ||
2219 | exit(1); | ||
2220 | } | ||
1774 | 2221 | ||
1775 | // check trace configuration | 2222 | // check trace configuration |
1776 | if (arg_trace && arg_tracelog) | 2223 | if (arg_trace && arg_tracelog) { |
1777 | fprintf(stderr, "Warning: --trace and --tracelog are mutually exclusive; --tracelog disabled\n"); | 2224 | if (!arg_quiet || arg_debug) |
2225 | fprintf(stderr, "Warning: --trace and --tracelog are mutually exclusive; --tracelog disabled\n"); | ||
2226 | } | ||
2227 | |||
2228 | // disable x11 abstract socket | ||
2229 | if (getenv("FIREJAIL_X11")) | ||
2230 | mask_x11_abstract_socket = 1; | ||
1778 | 2231 | ||
1779 | // check user namespace (--noroot) options | 2232 | // check user namespace (--noroot) options |
1780 | if (arg_noroot) { | 2233 | if (arg_noroot) { |
@@ -1798,66 +2251,41 @@ int main(int argc, char **argv) { | |||
1798 | free(msg); | 2251 | free(msg); |
1799 | } | 2252 | } |
1800 | 2253 | ||
1801 | // build the sandbox command | 2254 | // guess shell if unspecified |
1802 | if (prog_index == -1 && arg_zsh) { | 2255 | if (!arg_shell_none && !cfg.shell) { |
1803 | cfg.command_line = "/usr/bin/zsh"; | 2256 | cfg.shell = guess_shell(); |
1804 | cfg.window_title = "/usr/bin/zsh"; | 2257 | if (!cfg.shell) { |
1805 | cfg.command_name = "zsh"; | 2258 | fprintf(stderr, "Error: unable to guess your shell, please set explicitly by using --shell option.\n"); |
1806 | } | 2259 | exit(1); |
1807 | else if (prog_index == -1 && arg_csh) { | 2260 | } |
1808 | cfg.command_line = "/bin/csh"; | 2261 | if (arg_debug) |
1809 | cfg.window_title = "/bin/csh"; | 2262 | printf("Autoselecting %s as shell\n", cfg.shell); |
1810 | cfg.command_name = "csh"; | ||
1811 | } | 2263 | } |
1812 | else if (prog_index == -1 && cfg.shell) { | 2264 | |
2265 | // build the sandbox command | ||
2266 | if (prog_index == -1 && cfg.shell) { | ||
1813 | cfg.command_line = cfg.shell; | 2267 | cfg.command_line = cfg.shell; |
1814 | cfg.window_title = cfg.shell; | 2268 | cfg.window_title = cfg.shell; |
1815 | cfg.command_name = cfg.shell; | 2269 | cfg.command_name = cfg.shell; |
1816 | } | 2270 | } |
1817 | else if (prog_index == -1) { | 2271 | else if (arg_appimage) { |
1818 | cfg.command_line = "/bin/bash"; | 2272 | if (arg_debug) |
1819 | cfg.window_title = "/bin/bash"; | 2273 | printf("Configuring appimage environment\n"); |
1820 | cfg.command_name = "bash"; | 2274 | appimage_set(cfg.command_name); |
2275 | cfg.window_title = "appimage"; | ||
1821 | } | 2276 | } |
1822 | else { | 2277 | else { |
1823 | // calculate the length of the command | 2278 | build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index); |
1824 | int i; | ||
1825 | int len = 0; | ||
1826 | int argcnt = argc - prog_index; | ||
1827 | for (i = 0; i < argcnt; i++) | ||
1828 | len += strlen(argv[i + prog_index]) + 3; // + ' ' + 2 '"' | ||
1829 | |||
1830 | // build the string | ||
1831 | cfg.command_line = malloc(len + 1); // + '\0' | ||
1832 | if (!cfg.command_line) | ||
1833 | errExit("malloc"); | ||
1834 | cfg.window_title = malloc(len + 1); // + '\0' | ||
1835 | if (!cfg.window_title) | ||
1836 | errExit("malloc"); | ||
1837 | |||
1838 | char *ptr1 = cfg.command_line; | ||
1839 | char *ptr2 = cfg.window_title; | ||
1840 | for (i = 0; i < argcnt; i++) { | ||
1841 | // detect bash commands | ||
1842 | if (strstr(argv[i + prog_index], "&&") || strstr(argv[i + prog_index], "||")) { | ||
1843 | sprintf(ptr1, "%s ", argv[i + prog_index]); | ||
1844 | } | ||
1845 | else if (arg_command){ | ||
1846 | sprintf(ptr1, "%s ", argv[i + prog_index]); | ||
1847 | } | ||
1848 | else { | ||
1849 | sprintf(ptr1, "\"%s\" ", argv[i + prog_index]); | ||
1850 | } | ||
1851 | sprintf(ptr2, "%s ", argv[i + prog_index]); | ||
1852 | |||
1853 | ptr1 += strlen(ptr1); | ||
1854 | ptr2 += strlen(ptr2); | ||
1855 | } | ||
1856 | } | 2279 | } |
2280 | /* else { | ||
2281 | fprintf(stderr, "Error: command must be specified when --shell=none used.\n"); | ||
2282 | exit(1); | ||
2283 | }*/ | ||
1857 | 2284 | ||
1858 | assert(cfg.command_name); | 2285 | assert(cfg.command_name); |
1859 | if (arg_debug) | 2286 | if (arg_debug) |
1860 | printf("Command name #%s#\n", cfg.command_name); | 2287 | printf("Command name #%s#\n", cfg.command_name); |
2288 | |||
1861 | 2289 | ||
1862 | // load the profile | 2290 | // load the profile |
1863 | if (!arg_noprofile) { | 2291 | if (!arg_noprofile) { |
@@ -1881,14 +2309,16 @@ int main(int argc, char **argv) { | |||
1881 | } | 2309 | } |
1882 | } | 2310 | } |
1883 | 2311 | ||
1884 | // use generic.profile as the default | 2312 | // use default.profile as the default |
1885 | if (!custom_profile && !arg_noprofile) { | 2313 | if (!custom_profile && !arg_noprofile) { |
1886 | if (cfg.chrootdir) | 2314 | if (cfg.chrootdir) { |
1887 | fprintf(stderr, "Warning: default profile disabled by --chroot option\n"); | 2315 | if (!arg_quiet || arg_debug) |
1888 | else if (arg_overlay) | 2316 | fprintf(stderr, "Warning: default profile disabled by --chroot option\n"); |
1889 | fprintf(stderr, "Warning: default profile disabled by --overlay option\n"); | 2317 | } |
1890 | // else if (cfg.home_private_keep) | 2318 | else if (arg_overlay) { |
1891 | // fprintf(stderr, "Warning: default profile disabled by --private-home option\n"); | 2319 | if (!arg_quiet || arg_debug) |
2320 | fprintf(stderr, "Warning: default profile disabled by --overlay option\n"); | ||
2321 | } | ||
1892 | else { | 2322 | else { |
1893 | // try to load a default profile | 2323 | // try to load a default profile |
1894 | char *profile_name = DEFAULT_USER_PROFILE; | 2324 | char *profile_name = DEFAULT_USER_PROFILE; |
@@ -1911,12 +2341,20 @@ int main(int argc, char **argv) { | |||
1911 | else | 2341 | else |
1912 | custom_profile = profile_find(profile_name, SYSCONFDIR); | 2342 | custom_profile = profile_find(profile_name, SYSCONFDIR); |
1913 | } | 2343 | } |
2344 | if (!custom_profile) { | ||
2345 | fprintf(stderr, "Error: no default.profile installed\n"); | ||
2346 | exit(1); | ||
2347 | } | ||
1914 | 2348 | ||
1915 | if (custom_profile && !arg_quiet) | 2349 | if (custom_profile && !arg_quiet) |
1916 | printf("\n** Note: you can use --noprofile to disable %s.profile **\n\n", profile_name); | 2350 | printf("\n** Note: you can use --noprofile to disable %s.profile **\n\n", profile_name); |
1917 | } | 2351 | } |
1918 | } | 2352 | } |
1919 | 2353 | ||
2354 | // block X11 sockets | ||
2355 | if (arg_x11_block) | ||
2356 | x11_block(); | ||
2357 | |||
1920 | // check network configuration options - it will exit if anything went wrong | 2358 | // check network configuration options - it will exit if anything went wrong |
1921 | net_check_cfg(); | 2359 | net_check_cfg(); |
1922 | 2360 | ||
@@ -1947,11 +2385,13 @@ int main(int argc, char **argv) { | |||
1947 | errExit("pipe"); | 2385 | errExit("pipe"); |
1948 | 2386 | ||
1949 | if (arg_noroot && arg_overlay) { | 2387 | if (arg_noroot && arg_overlay) { |
1950 | fprintf(stderr, "Warning: --overlay and --noroot are mutually exclusive, noroot disabled\n"); | 2388 | if (!arg_quiet || arg_debug) |
2389 | fprintf(stderr, "Warning: --overlay and --noroot are mutually exclusive, noroot disabled\n"); | ||
1951 | arg_noroot = 0; | 2390 | arg_noroot = 0; |
1952 | } | 2391 | } |
1953 | else if (arg_noroot && cfg.chrootdir) { | 2392 | else if (arg_noroot && cfg.chrootdir) { |
1954 | fprintf(stderr, "Warning: --chroot and --noroot are mutually exclusive, noroot disabled\n"); | 2393 | if (!arg_quiet || arg_debug) |
2394 | fprintf(stderr, "Warning: --chroot and --noroot are mutually exclusive, noroot disabled\n"); | ||
1955 | arg_noroot = 0; | 2395 | arg_noroot = 0; |
1956 | } | 2396 | } |
1957 | 2397 | ||
@@ -2012,7 +2452,10 @@ int main(int argc, char **argv) { | |||
2012 | network_main(child); | 2452 | network_main(child); |
2013 | if (arg_debug) | 2453 | if (arg_debug) |
2014 | printf("Host network configured\n"); | 2454 | printf("Host network configured\n"); |
2015 | exit(0); | 2455 | #ifdef HAVE_GCOV |
2456 | __gcov_flush(); | ||
2457 | #endif | ||
2458 | _exit(0); | ||
2016 | } | 2459 | } |
2017 | 2460 | ||
2018 | // wait for the child to finish | 2461 | // wait for the child to finish |
@@ -2061,16 +2504,30 @@ int main(int argc, char **argv) { | |||
2061 | ptr += strlen(ptr); | 2504 | ptr += strlen(ptr); |
2062 | 2505 | ||
2063 | // add tty group | 2506 | // add tty group |
2064 | gid_t ttygid = get_tty_gid(); | 2507 | gid_t g = get_group_id("tty"); |
2065 | if (ttygid) { | 2508 | if (g) { |
2066 | sprintf(ptr, "%d %d 1\n", ttygid, ttygid); | 2509 | sprintf(ptr, "%d %d 1\n", g, g); |
2067 | ptr += strlen(ptr); | 2510 | ptr += strlen(ptr); |
2068 | } | 2511 | } |
2069 | 2512 | ||
2070 | // add audio group | 2513 | // add audio group |
2071 | gid_t audiogid = get_audio_gid(); | 2514 | g = get_group_id("audio"); |
2072 | if (ttygid) { | 2515 | if (g) { |
2073 | sprintf(ptr, "%d %d 1\n", audiogid, audiogid); | 2516 | sprintf(ptr, "%d %d 1\n", g, g); |
2517 | ptr += strlen(ptr); | ||
2518 | } | ||
2519 | |||
2520 | // add video group | ||
2521 | g = get_group_id("video"); | ||
2522 | if (g) { | ||
2523 | sprintf(ptr, "%d %d 1\n", g, g); | ||
2524 | ptr += strlen(ptr); | ||
2525 | } | ||
2526 | |||
2527 | // add games group | ||
2528 | g = get_group_id("games"); | ||
2529 | if (g) { | ||
2530 | sprintf(ptr, "%d %d 1\n", g, g); | ||
2074 | } | 2531 | } |
2075 | 2532 | ||
2076 | EUID_ROOT(); | 2533 | EUID_ROOT(); |
@@ -2084,8 +2541,10 @@ int main(int argc, char **argv) { | |||
2084 | close(parent_to_child_fds[1]); | 2541 | close(parent_to_child_fds[1]); |
2085 | 2542 | ||
2086 | EUID_ROOT(); | 2543 | EUID_ROOT(); |
2087 | if (lockfd != -1) | 2544 | if (lockfd != -1) { |
2088 | flock(lockfd, LOCK_UN); | 2545 | flock(lockfd, LOCK_UN); |
2546 | close(lockfd); | ||
2547 | } | ||
2089 | 2548 | ||
2090 | // create name file under /run/firejail | 2549 | // create name file under /run/firejail |
2091 | 2550 | ||
@@ -2101,13 +2560,6 @@ int main(int argc, char **argv) { | |||
2101 | waitpid(child, &status, 0); | 2560 | waitpid(child, &status, 0); |
2102 | 2561 | ||
2103 | // free globals | 2562 | // free globals |
2104 | #ifdef HAVE_SECCOMP | ||
2105 | if (cfg.seccomp_list_errno) { | ||
2106 | for (i = 0; i < highest_errno; i++) | ||
2107 | free(cfg.seccomp_list_errno[i]); | ||
2108 | free(cfg.seccomp_list_errno); | ||
2109 | } | ||
2110 | #endif | ||
2111 | if (cfg.profile) { | 2563 | if (cfg.profile) { |
2112 | ProfileEntry *prf = cfg.profile; | 2564 | ProfileEntry *prf = cfg.profile; |
2113 | while (prf != NULL) { | 2565 | while (prf != NULL) { |
diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c index 71abfb53d..1df4b7a0f 100644 --- a/src/firejail/netfilter.c +++ b/src/firejail/netfilter.c | |||
@@ -66,6 +66,8 @@ void netfilter(const char *fname) { | |||
66 | 66 | ||
67 | // custom filter | 67 | // custom filter |
68 | int allocated = 0; | 68 | int allocated = 0; |
69 | if (netfilter_default) | ||
70 | fname = netfilter_default; | ||
69 | if (fname) { | 71 | if (fname) { |
70 | // buffer the filter | 72 | // buffer the filter |
71 | struct stat s; | 73 | struct stat s; |
@@ -141,9 +143,10 @@ void netfilter(const char *fname) { | |||
141 | dup2(fd,STDIN_FILENO); | 143 | dup2(fd,STDIN_FILENO); |
142 | 144 | ||
143 | // wipe out environment variables | 145 | // wipe out environment variables |
144 | environ = NULL; | 146 | clearenv(); |
145 | execl(iptables_restore, iptables_restore, NULL); | 147 | execl(iptables_restore, iptables_restore, NULL); |
146 | // it will never get here!!! | 148 | perror("execl"); |
149 | _exit(1); | ||
147 | } | 150 | } |
148 | // wait for the child to finish | 151 | // wait for the child to finish |
149 | waitpid(child, NULL, 0); | 152 | waitpid(child, NULL, 0); |
@@ -160,8 +163,10 @@ void netfilter(const char *fname) { | |||
160 | if (setregid(0, 0)) | 163 | if (setregid(0, 0)) |
161 | errExit("setregid"); | 164 | errExit("setregid"); |
162 | environ = NULL; | 165 | environ = NULL; |
166 | assert(getenv("LD_PRELOAD") == NULL); | ||
163 | execl(iptables, iptables, "-vL", NULL); | 167 | execl(iptables, iptables, "-vL", NULL); |
164 | // it will never get here!!! | 168 | perror("execl"); |
169 | _exit(1); | ||
165 | } | 170 | } |
166 | // wait for the child to finish | 171 | // wait for the child to finish |
167 | waitpid(child, NULL, 0); | 172 | waitpid(child, NULL, 0); |
@@ -252,9 +257,10 @@ void netfilter6(const char *fname) { | |||
252 | dup2(fd,STDIN_FILENO); | 257 | dup2(fd,STDIN_FILENO); |
253 | 258 | ||
254 | // wipe out environment variables | 259 | // wipe out environment variables |
255 | environ = NULL; | 260 | clearenv(); |
256 | execl(ip6tables_restore, ip6tables_restore, NULL); | 261 | execl(ip6tables_restore, ip6tables_restore, NULL); |
257 | // it will never get here!!! | 262 | perror("execl"); |
263 | _exit(1); | ||
258 | } | 264 | } |
259 | // wait for the child to finish | 265 | // wait for the child to finish |
260 | waitpid(child, NULL, 0); | 266 | waitpid(child, NULL, 0); |
@@ -265,9 +271,10 @@ void netfilter6(const char *fname) { | |||
265 | if (child < 0) | 271 | if (child < 0) |
266 | errExit("fork"); | 272 | errExit("fork"); |
267 | if (child == 0) { | 273 | if (child == 0) { |
268 | environ = NULL; | 274 | clearenv(); |
269 | execl(ip6tables, ip6tables, "-vL", NULL); | 275 | execl(ip6tables, ip6tables, "-vL", NULL); |
270 | // it will never get here!!! | 276 | perror("execl"); |
277 | _exit(1); | ||
271 | } | 278 | } |
272 | // wait for the child to finish | 279 | // wait for the child to finish |
273 | waitpid(child, NULL, 0); | 280 | waitpid(child, NULL, 0); |
diff --git a/src/firejail/network.c b/src/firejail/network.c index aac48e521..6d09d770f 100644 --- a/src/firejail/network.c +++ b/src/firejail/network.c | |||
@@ -28,70 +28,6 @@ | |||
28 | #include <net/route.h> | 28 | #include <net/route.h> |
29 | #include <linux/if_bridge.h> | 29 | #include <linux/if_bridge.h> |
30 | 30 | ||
31 | // scan interfaces in current namespace and print IP address/mask for each interface | ||
32 | void net_ifprint(void) { | ||
33 | uint32_t ip; | ||
34 | uint32_t mask; | ||
35 | struct ifaddrs *ifaddr, *ifa; | ||
36 | |||
37 | if (getifaddrs(&ifaddr) == -1) | ||
38 | errExit("getifaddrs"); | ||
39 | |||
40 | printf("%-17.17s%-19.19s%-17.17s%-17.17s%-6.6s\n", | ||
41 | "Interface", "MAC", "IP", "Mask", "Status"); | ||
42 | // walk through the linked list | ||
43 | for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { | ||
44 | if (ifa->ifa_addr == NULL) | ||
45 | continue; | ||
46 | |||
47 | if (ifa->ifa_addr->sa_family == AF_INET) { | ||
48 | struct sockaddr_in *si = (struct sockaddr_in *) ifa->ifa_netmask; | ||
49 | mask = ntohl(si->sin_addr.s_addr); | ||
50 | si = (struct sockaddr_in *) ifa->ifa_addr; | ||
51 | ip = ntohl(si->sin_addr.s_addr); | ||
52 | |||
53 | // interface status | ||
54 | char *status; | ||
55 | if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) | ||
56 | status = "UP"; | ||
57 | else | ||
58 | status = "DOWN"; | ||
59 | |||
60 | // ip address and mask | ||
61 | char ipstr[30]; | ||
62 | sprintf(ipstr, "%d.%d.%d.%d", PRINT_IP(ip)); | ||
63 | char maskstr[30]; | ||
64 | sprintf(maskstr, "%d.%d.%d.%d", PRINT_IP(mask)); | ||
65 | |||
66 | // mac address | ||
67 | unsigned char mac[6]; | ||
68 | net_get_mac(ifa->ifa_name, mac); | ||
69 | char macstr[30]; | ||
70 | if (strcmp(ifa->ifa_name, "lo") == 0) | ||
71 | macstr[0] = '\0'; | ||
72 | else | ||
73 | sprintf(macstr, "%02x:%02x:%02x:%02x:%02x:%02x", PRINT_MAC(mac)); | ||
74 | |||
75 | |||
76 | printf("%-17.17s%-19.19s%-17.17s%-17.17s%-6.6s\n", | ||
77 | ifa->ifa_name, macstr, ipstr, maskstr, status); | ||
78 | |||
79 | // network scanning | ||
80 | if (!arg_scan) // scanning disabled | ||
81 | continue; | ||
82 | if (strcmp(ifa->ifa_name, "lo") == 0) // no loopbabck scanning | ||
83 | continue; | ||
84 | if (mask2bits(mask) < 16) // not scanning large networks | ||
85 | continue; | ||
86 | if (!ip) // if not configured | ||
87 | continue; | ||
88 | // only if the interface is up and running | ||
89 | if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) | ||
90 | arp_scan(ifa->ifa_name, ip, mask); | ||
91 | } | ||
92 | } | ||
93 | freeifaddrs(ifaddr); | ||
94 | } | ||
95 | 31 | ||
96 | int net_get_mtu(const char *ifname) { | 32 | int net_get_mtu(const char *ifname) { |
97 | int mtu = 0; | 33 | int mtu = 0; |
@@ -190,95 +126,11 @@ void net_if_up(const char *ifname) { | |||
190 | fprintf(stderr, "Error: invalid network device name %s\n", ifname); | 126 | fprintf(stderr, "Error: invalid network device name %s\n", ifname); |
191 | exit(1); | 127 | exit(1); |
192 | } | 128 | } |
193 | 129 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, | |
194 | int sock = socket(AF_INET,SOCK_DGRAM,0); | 130 | PATH_FNET, "ifup", ifname); |
195 | if (sock < 0) | 131 | } |
196 | errExit("socket"); | ||
197 | |||
198 | // get the existing interface flags | ||
199 | struct ifreq ifr; | ||
200 | memset(&ifr, 0, sizeof(ifr)); | ||
201 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
202 | ifr.ifr_addr.sa_family = AF_INET; | ||
203 | |||
204 | // read the existing flags | ||
205 | if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { | ||
206 | close(sock); | ||
207 | errExit("ioctl"); | ||
208 | } | ||
209 | 132 | ||
210 | ifr.ifr_flags |= IFF_UP; | ||
211 | 133 | ||
212 | // set the new flags | ||
213 | if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) { | ||
214 | close(sock); | ||
215 | errExit("ioctl"); | ||
216 | } | ||
217 | |||
218 | // checking | ||
219 | // read the existing flags | ||
220 | if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { | ||
221 | close(sock); | ||
222 | errExit("ioctl"); | ||
223 | } | ||
224 | |||
225 | // wait not more than 500ms for the interface to come up | ||
226 | int cnt = 0; | ||
227 | while (cnt < 50) { | ||
228 | usleep(10000); // sleep 10ms | ||
229 | |||
230 | // read the existing flags | ||
231 | if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { | ||
232 | close(sock); | ||
233 | errExit("ioctl"); | ||
234 | } | ||
235 | if (ifr.ifr_flags & IFF_RUNNING) | ||
236 | break; | ||
237 | cnt++; | ||
238 | } | ||
239 | |||
240 | close(sock); | ||
241 | } | ||
242 | |||
243 | // bring interface up | ||
244 | void net_if_down(const char *ifname) { | ||
245 | if (strlen(ifname) > IFNAMSIZ) { | ||
246 | fprintf(stderr, "Error: invalid network device name %s\n", ifname); | ||
247 | exit(1); | ||
248 | } | ||
249 | |||
250 | int sock = socket(AF_INET,SOCK_DGRAM,0); | ||
251 | if (sock < 0) | ||
252 | errExit("socket"); | ||
253 | |||
254 | // get the existing interface flags | ||
255 | struct ifreq ifr; | ||
256 | memset(&ifr, 0, sizeof(ifr)); | ||
257 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
258 | ifr.ifr_addr.sa_family = AF_INET; | ||
259 | |||
260 | // read the existing flags | ||
261 | if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { | ||
262 | close(sock); | ||
263 | errExit("ioctl"); | ||
264 | } | ||
265 | |||
266 | ifr.ifr_flags &= ~IFF_UP; | ||
267 | |||
268 | // set the new flags | ||
269 | if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) { | ||
270 | close(sock); | ||
271 | errExit("ioctl"); | ||
272 | } | ||
273 | |||
274 | close(sock); | ||
275 | } | ||
276 | |||
277 | struct ifreq6 { | ||
278 | struct in6_addr ifr6_addr; | ||
279 | uint32_t ifr6_prefixlen; | ||
280 | unsigned int ifr6_ifindex; | ||
281 | }; | ||
282 | // configure interface ipv6 address | 134 | // configure interface ipv6 address |
283 | // ex: firejail --net=eth0 --ip6=2001:0db8:0:f101::1/64 | 135 | // ex: firejail --net=eth0 --ip6=2001:0db8:0:f101::1/64 |
284 | void net_if_ip6(const char *ifname, const char *addr6) { | 136 | void net_if_ip6(const char *ifname, const char *addr6) { |
@@ -287,107 +139,11 @@ void net_if_ip6(const char *ifname, const char *addr6) { | |||
287 | exit(1); | 139 | exit(1); |
288 | } | 140 | } |
289 | 141 | ||
290 | // extract prefix | 142 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 5, |
291 | unsigned long prefix; | 143 | PATH_FNET, "config", "ipv6", ifname, addr6); |
292 | char *ptr; | ||
293 | if ((ptr = strchr(addr6, '/'))) { | ||
294 | prefix = atol(ptr + 1); | ||
295 | if (prefix > 128) { | ||
296 | fprintf(stderr, "Error: invalid prefix for IPv6 address %s\n", addr6); | ||
297 | exit(1); | ||
298 | } | ||
299 | *ptr = '\0'; // mark the end of the address | ||
300 | } | ||
301 | else | ||
302 | prefix = 128; | ||
303 | |||
304 | // extract address | ||
305 | struct sockaddr_in6 sin6; | ||
306 | memset(&sin6, 0, sizeof(sin6)); | ||
307 | sin6.sin6_family = AF_INET6; | ||
308 | int rv = inet_pton(AF_INET6, addr6, sin6.sin6_addr.s6_addr); | ||
309 | if (rv <= 0) { | ||
310 | fprintf(stderr, "Error: invalid IPv6 address %s\n", addr6); | ||
311 | exit(1); | ||
312 | } | ||
313 | |||
314 | // open socket | ||
315 | int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); | ||
316 | if (sock < 0) { | ||
317 | fprintf(stderr, "Error: IPv6 is not supported on this system\n"); | ||
318 | exit(1); | ||
319 | } | ||
320 | |||
321 | // find interface index | ||
322 | struct ifreq ifr; | ||
323 | memset(&ifr, 0, sizeof(ifr)); | ||
324 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
325 | ifr.ifr_addr.sa_family = AF_INET; | ||
326 | if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) { | ||
327 | perror("ioctl SIOGIFINDEX"); | ||
328 | exit(1); | ||
329 | } | ||
330 | |||
331 | // configure address | ||
332 | struct ifreq6 ifr6; | ||
333 | memset(&ifr6, 0, sizeof(ifr6)); | ||
334 | ifr6.ifr6_prefixlen = prefix; | ||
335 | ifr6.ifr6_ifindex = ifr.ifr_ifindex; | ||
336 | memcpy((char *) &ifr6.ifr6_addr, (char *) &sin6.sin6_addr, sizeof(struct in6_addr)); | ||
337 | if (ioctl(sock, SIOCSIFADDR, &ifr6) < 0) { | ||
338 | perror("ioctl SIOCSIFADDR"); | ||
339 | exit(1); | ||
340 | } | ||
341 | |||
342 | close(sock); | ||
343 | } | ||
344 | |||
345 | // configure interface ipv4 address | ||
346 | void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu) { | ||
347 | if (strlen(ifname) > IFNAMSIZ) { | ||
348 | fprintf(stderr, "Error: invalid network device name %s\n", ifname); | ||
349 | exit(1); | ||
350 | } | ||
351 | if (arg_debug) | ||
352 | printf("configure interface %s\n", ifname); | ||
353 | |||
354 | int sock = socket(AF_INET,SOCK_DGRAM,0); | ||
355 | if (sock < 0) | ||
356 | errExit("socket"); | ||
357 | |||
358 | struct ifreq ifr; | ||
359 | memset(&ifr, 0, sizeof(ifr)); | ||
360 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
361 | ifr.ifr_addr.sa_family = AF_INET; | ||
362 | |||
363 | ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip); | ||
364 | if (ioctl( sock, SIOCSIFADDR, &ifr ) < 0) { | ||
365 | close(sock); | ||
366 | errExit("ioctl"); | ||
367 | } | ||
368 | |||
369 | if (ip != 0) { | ||
370 | ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(mask); | ||
371 | if (ioctl( sock, SIOCSIFNETMASK, &ifr ) < 0) { | ||
372 | close(sock); | ||
373 | errExit("ioctl"); | ||
374 | } | ||
375 | } | ||
376 | |||
377 | // configure mtu | ||
378 | if (mtu > 0) { | ||
379 | ifr.ifr_mtu = mtu; | ||
380 | if (ioctl( sock, SIOCSIFMTU, &ifr ) < 0) { | ||
381 | close(sock); | ||
382 | errExit("ioctl"); | ||
383 | } | ||
384 | } | ||
385 | 144 | ||
386 | close(sock); | ||
387 | usleep(10000); // sleep 10ms | ||
388 | } | 145 | } |
389 | 146 | ||
390 | |||
391 | // add an IP route, return -1 if error, 0 if the route was added | 147 | // add an IP route, return -1 if error, 0 if the route was added |
392 | int net_add_route(uint32_t ip, uint32_t mask, uint32_t gw) { | 148 | int net_add_route(uint32_t ip, uint32_t mask, uint32_t gw) { |
393 | int sock; | 149 | int sock; |
@@ -425,52 +181,6 @@ int net_add_route(uint32_t ip, uint32_t mask, uint32_t gw) { | |||
425 | } | 181 | } |
426 | 182 | ||
427 | 183 | ||
428 | // add a veth device to a bridge | ||
429 | void net_bridge_add_interface(const char *bridge, const char *dev) { | ||
430 | if (strlen(bridge) > IFNAMSIZ) { | ||
431 | fprintf(stderr, "Error: invalid network device name %s\n", bridge); | ||
432 | exit(1); | ||
433 | } | ||
434 | |||
435 | // somehow adding the interface to the bridge resets MTU on bridge device!!! | ||
436 | // workaround: restore MTU on the bridge device | ||
437 | // todo: put a real fix in | ||
438 | int mtu1 = net_get_mtu(bridge); | ||
439 | |||
440 | struct ifreq ifr; | ||
441 | int err; | ||
442 | int ifindex = if_nametoindex(dev); | ||
443 | |||
444 | if (ifindex <= 0) | ||
445 | errExit("if_nametoindex"); | ||
446 | |||
447 | int sock; | ||
448 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) | ||
449 | errExit("socket"); | ||
450 | |||
451 | memset(&ifr, 0, sizeof(ifr)); | ||
452 | strncpy(ifr.ifr_name, bridge, IFNAMSIZ); | ||
453 | #ifdef SIOCBRADDIF | ||
454 | ifr.ifr_ifindex = ifindex; | ||
455 | err = ioctl(sock, SIOCBRADDIF, &ifr); | ||
456 | if (err < 0) | ||
457 | #endif | ||
458 | { | ||
459 | unsigned long args[4] = { BRCTL_ADD_IF, ifindex, 0, 0 }; | ||
460 | |||
461 | ifr.ifr_data = (char *) args; | ||
462 | err = ioctl(sock, SIOCDEVPRIVATE, &ifr); | ||
463 | } | ||
464 | (void) err; | ||
465 | close(sock); | ||
466 | |||
467 | int mtu2 = net_get_mtu(bridge); | ||
468 | if (mtu1 != mtu2) { | ||
469 | if (arg_debug) | ||
470 | printf("Restoring MTU for %s\n", bridge); | ||
471 | net_set_mtu(bridge, mtu1); | ||
472 | } | ||
473 | } | ||
474 | 184 | ||
475 | #define BUFSIZE 1024 | 185 | #define BUFSIZE 1024 |
476 | uint32_t network_get_defaultgw(void) { | 186 | uint32_t network_get_defaultgw(void) { |
@@ -504,20 +214,15 @@ uint32_t network_get_defaultgw(void) { | |||
504 | } | 214 | } |
505 | 215 | ||
506 | int net_config_mac(const char *ifname, const unsigned char mac[6]) { | 216 | int net_config_mac(const char *ifname, const unsigned char mac[6]) { |
507 | struct ifreq ifr; | 217 | char *macstr; |
508 | int sock; | 218 | if (asprintf(&macstr, "%02x:%02x:%02x:%02x:%02x:%02x", |
509 | 219 | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) == -1) | |
510 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) | 220 | errExit("asprintf"); |
511 | errExit("socket"); | ||
512 | 221 | ||
513 | memset(&ifr, 0, sizeof(ifr)); | 222 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 5, |
514 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | 223 | PATH_FNET, "config", "mac", ifname, macstr); |
515 | ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; | ||
516 | memcpy(ifr.ifr_hwaddr.sa_data, mac, 6); | ||
517 | 224 | ||
518 | if (ioctl(sock, SIOCSIFHWADDR, &ifr) == -1) | 225 | free(macstr); |
519 | errExit("ioctl"); | ||
520 | close(sock); | ||
521 | return 0; | 226 | return 0; |
522 | } | 227 | } |
523 | 228 | ||
@@ -540,3 +245,27 @@ int net_get_mac(const char *ifname, unsigned char mac[6]) { | |||
540 | close(sock); | 245 | close(sock); |
541 | return 0; | 246 | return 0; |
542 | } | 247 | } |
248 | |||
249 | void net_config_interface(const char *dev, uint32_t ip, uint32_t mask, int mtu) { | ||
250 | assert(dev); | ||
251 | |||
252 | char *ipstr; | ||
253 | if (asprintf(&ipstr, "%llu", (long long unsigned) ip) == -1) | ||
254 | errExit("asprintf"); | ||
255 | |||
256 | char *maskstr; | ||
257 | if (asprintf(&maskstr, "%llu", (long long unsigned) mask) == -1) | ||
258 | errExit("asprintf"); | ||
259 | |||
260 | char *mtustr; | ||
261 | if (asprintf(&mtustr, "%d", mtu) == -1) | ||
262 | errExit("asprintf"); | ||
263 | |||
264 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 7, | ||
265 | PATH_FNET, "config", "interface", dev, ipstr, maskstr, mtustr); | ||
266 | |||
267 | free(ipstr); | ||
268 | free(maskstr); | ||
269 | free(mtustr); | ||
270 | } | ||
271 | |||
diff --git a/src/firejail/network.txt b/src/firejail/network.txt index 673d5b941..f6df0f485 100644 --- a/src/firejail/network.txt +++ b/src/firejail/network.txt | |||
@@ -13,7 +13,7 @@ net_configure_bridge(br, device) { | |||
13 | } | 13 | } |
14 | 14 | ||
15 | net_configure_sandbox_ip(br) { | 15 | net_configure_sandbox_ip(br) { |
16 | if br->ip_snadbox | 16 | if br->ip_sandbox |
17 | check br->ipsandbox inside the bridge network | 17 | check br->ipsandbox inside the bridge network |
18 | arp_check(br->ipsandbox) // send an arp req to check if anybody else is using this address | 18 | arp_check(br->ipsandbox) // send an arp req to check if anybody else is using this address |
19 | else | 19 | else |
diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c index e6d5cd5d7..9fbc09d2b 100644 --- a/src/firejail/network_main.c +++ b/src/firejail/network_main.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <sys/stat.h> | 23 | #include <sys/stat.h> |
24 | #include <unistd.h> | 24 | #include <unistd.h> |
25 | #include <net/if.h> | 25 | #include <net/if.h> |
26 | #include <stdarg.h> | ||
26 | 27 | ||
27 | // configure bridge structure | 28 | // configure bridge structure |
28 | // - extract ip address and mask from the bridge interface | 29 | // - extract ip address and mask from the bridge interface |
@@ -56,9 +57,12 @@ void net_configure_bridge(Bridge *br, char *dev_name) { | |||
56 | } | 57 | } |
57 | } | 58 | } |
58 | 59 | ||
60 | // allow unconfigured interfaces | ||
59 | if (net_get_if_addr(br->dev, &br->ip, &br->mask, br->mac, &br->mtu)) { | 61 | if (net_get_if_addr(br->dev, &br->ip, &br->mask, br->mac, &br->mtu)) { |
60 | fprintf(stderr, "Error: interface %s is not configured\n", br->dev); | 62 | fprintf(stderr, "Warning: the network interface %s is not configured\n", br->dev); |
61 | exit(1); | 63 | br->configured = 1; |
64 | br->arg_ip_none = 1; | ||
65 | return; | ||
62 | } | 66 | } |
63 | if (arg_debug) { | 67 | if (arg_debug) { |
64 | if (br->macvlan == 0) | 68 | if (br->macvlan == 0) |
@@ -117,15 +121,18 @@ void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child) { | |||
117 | 121 | ||
118 | // create a veth pair | 122 | // create a veth pair |
119 | char *dev; | 123 | char *dev; |
120 | if (asprintf(&dev, "veth%u%s", getpid(), ifname) < 0) | 124 | if (br->veth_name == NULL) { |
125 | if (asprintf(&dev, "veth%u%s", getpid(), ifname) < 0) | ||
126 | errExit("asprintf"); | ||
127 | } | ||
128 | else | ||
129 | dev = br->veth_name; | ||
130 | |||
131 | char *cstr; | ||
132 | if (asprintf(&cstr, "%d", child) == -1) | ||
121 | errExit("asprintf"); | 133 | errExit("asprintf"); |
122 | net_create_veth(dev, ifname, child); | 134 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 7, PATH_FNET, "create", "veth", dev, ifname, br->dev, cstr); |
123 | 135 | free(cstr); | |
124 | // add interface to the bridge | ||
125 | net_bridge_add_interface(br->dev, dev); | ||
126 | |||
127 | // bring up the interface | ||
128 | net_if_up(dev); | ||
129 | 136 | ||
130 | char *msg; | 137 | char *msg; |
131 | if (asprintf(&msg, "%d.%d.%d.%d address assigned to sandbox", PRINT_IP(br->ipsandbox)) == -1) | 138 | if (asprintf(&msg, "%d.%d.%d.%d address assigned to sandbox", PRINT_IP(br->ipsandbox)) == -1) |
@@ -224,23 +231,6 @@ void net_check_cfg(void) { | |||
224 | } | 231 | } |
225 | } | 232 | } |
226 | 233 | ||
227 | |||
228 | |||
229 | void net_dns_print_name(const char *name) { | ||
230 | EUID_ASSERT(); | ||
231 | if (!name || strlen(name) == 0) { | ||
232 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
233 | exit(1); | ||
234 | } | ||
235 | pid_t pid; | ||
236 | if (name2pid(name, &pid)) { | ||
237 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
238 | exit(1); | ||
239 | } | ||
240 | |||
241 | net_dns_print(pid); | ||
242 | } | ||
243 | |||
244 | #define MAXBUF 4096 | 234 | #define MAXBUF 4096 |
245 | void net_dns_print(pid_t pid) { | 235 | void net_dns_print(pid_t pid) { |
246 | EUID_ASSERT(); | 236 | EUID_ASSERT(); |
@@ -282,47 +272,53 @@ void net_dns_print(pid_t pid) { | |||
282 | } | 272 | } |
283 | 273 | ||
284 | void network_main(pid_t child) { | 274 | void network_main(pid_t child) { |
275 | char *cstr; | ||
276 | if (asprintf(&cstr, "%d", child) == -1) | ||
277 | errExit("asprintf"); | ||
278 | |||
285 | // create veth pair or macvlan device | 279 | // create veth pair or macvlan device |
286 | if (cfg.bridge0.configured) { | 280 | if (cfg.bridge0.configured) { |
287 | if (cfg.bridge0.macvlan == 0) { | 281 | if (cfg.bridge0.macvlan == 0) { |
288 | net_configure_veth_pair(&cfg.bridge0, "eth0", child); | 282 | net_configure_veth_pair(&cfg.bridge0, "eth0", child); |
289 | } | 283 | } |
290 | else | 284 | else |
291 | net_create_macvlan(cfg.bridge0.devsandbox, cfg.bridge0.dev, child); | 285 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge0.devsandbox, cfg.bridge0.dev, cstr); |
292 | } | 286 | } |
293 | 287 | ||
294 | if (cfg.bridge1.configured) { | 288 | if (cfg.bridge1.configured) { |
295 | if (cfg.bridge1.macvlan == 0) | 289 | if (cfg.bridge1.macvlan == 0) |
296 | net_configure_veth_pair(&cfg.bridge1, "eth1", child); | 290 | net_configure_veth_pair(&cfg.bridge1, "eth1", child); |
297 | else | 291 | else |
298 | net_create_macvlan(cfg.bridge1.devsandbox, cfg.bridge1.dev, child); | 292 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge1.devsandbox, cfg.bridge1.dev, cstr); |
299 | } | 293 | } |
300 | 294 | ||
301 | if (cfg.bridge2.configured) { | 295 | if (cfg.bridge2.configured) { |
302 | if (cfg.bridge2.macvlan == 0) | 296 | if (cfg.bridge2.macvlan == 0) |
303 | net_configure_veth_pair(&cfg.bridge2, "eth2", child); | 297 | net_configure_veth_pair(&cfg.bridge2, "eth2", child); |
304 | else | 298 | else |
305 | net_create_macvlan(cfg.bridge2.devsandbox, cfg.bridge2.dev, child); | 299 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge2.devsandbox, cfg.bridge2.dev, cstr); |
306 | } | 300 | } |
307 | 301 | ||
308 | if (cfg.bridge3.configured) { | 302 | if (cfg.bridge3.configured) { |
309 | if (cfg.bridge3.macvlan == 0) | 303 | if (cfg.bridge3.macvlan == 0) |
310 | net_configure_veth_pair(&cfg.bridge3, "eth3", child); | 304 | net_configure_veth_pair(&cfg.bridge3, "eth3", child); |
311 | else | 305 | else |
312 | net_create_macvlan(cfg.bridge3.devsandbox, cfg.bridge3.dev, child); | 306 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge3.devsandbox, cfg.bridge3.dev, cstr); |
313 | } | 307 | } |
314 | 308 | ||
315 | // move interfaces in sandbox | 309 | // move interfaces in sandbox |
316 | if (cfg.interface0.configured) { | 310 | if (cfg.interface0.configured) { |
317 | net_move_interface(cfg.interface0.dev, child); | 311 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface0.dev, cstr); |
318 | } | 312 | } |
319 | if (cfg.interface1.configured) { | 313 | if (cfg.interface1.configured) { |
320 | net_move_interface(cfg.interface1.dev, child); | 314 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface1.dev, cstr); |
321 | } | 315 | } |
322 | if (cfg.interface2.configured) { | 316 | if (cfg.interface2.configured) { |
323 | net_move_interface(cfg.interface2.dev, child); | 317 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); |
324 | } | 318 | } |
325 | if (cfg.interface3.configured) { | 319 | if (cfg.interface3.configured) { |
326 | net_move_interface(cfg.interface3.dev, child); | 320 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); |
327 | } | 321 | } |
322 | |||
323 | free(cstr); | ||
328 | } | 324 | } |
diff --git a/src/firejail/no_sandbox.c b/src/firejail/no_sandbox.c index a9242f035..aae490c34 100644 --- a/src/firejail/no_sandbox.c +++ b/src/firejail/no_sandbox.c | |||
@@ -23,6 +23,65 @@ | |||
23 | #include <unistd.h> | 23 | #include <unistd.h> |
24 | #include <grp.h> | 24 | #include <grp.h> |
25 | 25 | ||
26 | #define MAX_BUF 4096 | ||
27 | |||
28 | int is_container(const char *str) { | ||
29 | assert(str); | ||
30 | if (strcmp(str, "lxc") == 0 || | ||
31 | strcmp(str, "docker") == 0 || | ||
32 | strcmp(str, "lxc-libvirt") == 0 || | ||
33 | strcmp(str, "systemd-nspawn") == 0 || | ||
34 | strcmp(str, "rkt") == 0) | ||
35 | return 1; | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | // returns 1 if we are running under LXC | ||
40 | int check_namespace_virt(void) { | ||
41 | EUID_ASSERT(); | ||
42 | |||
43 | // check container environment variable | ||
44 | char *str = getenv("container"); | ||
45 | if (str && is_container(str)) | ||
46 | return 1; | ||
47 | |||
48 | // check PID 1 container environment variable | ||
49 | EUID_ROOT(); | ||
50 | FILE *fp = fopen("/proc/1/environ", "r"); | ||
51 | if (fp) { | ||
52 | int c = 0; | ||
53 | while (c != EOF) { | ||
54 | // read one line | ||
55 | char buf[MAX_BUF]; | ||
56 | int i = 0; | ||
57 | while ((c = fgetc(fp)) != EOF) { | ||
58 | if (c == 0) | ||
59 | break; | ||
60 | buf[i] = (char) c; | ||
61 | if (++i == (MAX_BUF - 1)) | ||
62 | break; | ||
63 | } | ||
64 | buf[i] = '\0'; | ||
65 | |||
66 | // check env var name | ||
67 | if (strncmp(buf, "container=", 10) == 0) { | ||
68 | // found it | ||
69 | if (is_container(buf + 10)) { | ||
70 | fclose(fp); | ||
71 | EUID_USER(); | ||
72 | return 1; | ||
73 | } | ||
74 | } | ||
75 | // printf("i %d c %d, buf #%s#\n", i, c, buf); | ||
76 | } | ||
77 | |||
78 | fclose(fp); | ||
79 | } | ||
80 | |||
81 | EUID_USER(); | ||
82 | return 0; | ||
83 | } | ||
84 | |||
26 | // check process space for kernel processes | 85 | // check process space for kernel processes |
27 | // return 1 if found, 0 if not found | 86 | // return 1 if found, 0 if not found |
28 | int check_kernel_procs(void) { | 87 | int check_kernel_procs(void) { |
@@ -103,44 +162,130 @@ int check_kernel_procs(void) { | |||
103 | void run_no_sandbox(int argc, char **argv) { | 162 | void run_no_sandbox(int argc, char **argv) { |
104 | EUID_ASSERT(); | 163 | EUID_ASSERT(); |
105 | 164 | ||
106 | // build command | 165 | // process limited subset of options |
107 | char *command = NULL; | 166 | int i; |
108 | int allocated = 0; | 167 | for (i = 0; i < argc; i++) { |
109 | if (argc == 1) | 168 | if (strcmp(argv[i], "--csh") == 0) { |
110 | command = "/bin/bash"; | 169 | if (arg_shell_none) { |
111 | else { | 170 | fprintf(stderr, "Error: --shell=none was already specified.\n"); |
112 | // calculate length | 171 | exit(1); |
113 | int len = 0; | 172 | } |
114 | int i; | 173 | if (cfg.shell) { |
115 | for (i = 1; i < argc; i++) { | 174 | fprintf(stderr, "Error: only one default user shell can be specified\n"); |
116 | if (*argv[i] == '-') | 175 | exit(1); |
117 | continue; | 176 | } |
177 | cfg.shell = "/bin/csh"; | ||
178 | } | ||
179 | else if (strcmp(argv[i], "--zsh") == 0) { | ||
180 | if (arg_shell_none) { | ||
181 | fprintf(stderr, "Error: --shell=none was already specified.\n"); | ||
182 | exit(1); | ||
183 | } | ||
184 | if (cfg.shell) { | ||
185 | fprintf(stderr, "Error: only one default user shell can be specified\n"); | ||
186 | exit(1); | ||
187 | } | ||
188 | cfg.shell = "/bin/zsh"; | ||
189 | } | ||
190 | else if (strcmp(argv[i], "--shell=none") == 0) { | ||
191 | arg_shell_none = 1; | ||
192 | if (cfg.shell) { | ||
193 | fprintf(stderr, "Error: a shell was already specified\n"); | ||
194 | exit(1); | ||
195 | } | ||
196 | } | ||
197 | else if (strncmp(argv[i], "--shell=", 8) == 0) { | ||
198 | if (arg_shell_none) { | ||
199 | fprintf(stderr, "Error: --shell=none was already specified.\n"); | ||
200 | exit(1); | ||
201 | } | ||
202 | invalid_filename(argv[i] + 8); | ||
203 | |||
204 | if (cfg.shell) { | ||
205 | fprintf(stderr, "Error: only one user shell can be specified\n"); | ||
206 | exit(1); | ||
207 | } | ||
208 | cfg.shell = argv[i] + 8; | ||
209 | |||
210 | if (is_dir(cfg.shell) || strstr(cfg.shell, "..")) { | ||
211 | fprintf(stderr, "Error: invalid shell\n"); | ||
212 | exit(1); | ||
213 | } | ||
214 | |||
215 | // access call checks as real UID/GID, not as effective UID/GID | ||
216 | if(cfg.chrootdir) { | ||
217 | char *shellpath; | ||
218 | if (asprintf(&shellpath, "%s%s", cfg.chrootdir, cfg.shell) == -1) | ||
219 | errExit("asprintf"); | ||
220 | if (access(shellpath, R_OK)) { | ||
221 | fprintf(stderr, "Error: cannot access shell file in chroot\n"); | ||
222 | exit(1); | ||
223 | } | ||
224 | free(shellpath); | ||
225 | } else if (access(cfg.shell, R_OK)) { | ||
226 | fprintf(stderr, "Error: cannot access shell file\n"); | ||
227 | exit(1); | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | |||
232 | // use $SHELL to get shell used in sandbox | ||
233 | if (!arg_shell_none && !cfg.shell) { | ||
234 | char *shell = getenv("SHELL"); | ||
235 | if (access(shell, R_OK) == 0) | ||
236 | cfg.shell = shell; | ||
237 | } | ||
238 | // guess shell otherwise | ||
239 | if (!arg_shell_none && !cfg.shell) { | ||
240 | cfg.shell = guess_shell(); | ||
241 | if (arg_debug) | ||
242 | printf("Autoselecting %s as shell\n", cfg.shell); | ||
243 | } | ||
244 | if (!arg_shell_none && !cfg.shell) { | ||
245 | fprintf(stderr, "Error: unable to guess your shell, please set explicitly by using --shell option.\n"); | ||
246 | exit(1); | ||
247 | } | ||
248 | |||
249 | int prog_index = 0; | ||
250 | // find first non option arg: | ||
251 | // - first argument not starting wiht --, | ||
252 | // - whatever follows after -c (example: firejail -c ls) | ||
253 | for (i = 1; i < argc; i++) { | ||
254 | if (strcmp(argv[i], "-c") == 0) { | ||
255 | prog_index = i + 1; | ||
256 | if (prog_index == argc) { | ||
257 | fprintf(stderr, "Error: option -c requires an argument\n"); | ||
258 | exit(1); | ||
259 | } | ||
118 | break; | 260 | break; |
119 | } | 261 | } |
120 | int start_index = i; | 262 | // check first argument not starting with -- |
121 | for (i = start_index; i < argc; i++) | 263 | if (strncmp(argv[i],"--",2) != 0) { |
122 | len += strlen(argv[i]) + 1; | 264 | prog_index = i; |
123 | 265 | break; | |
124 | // allocate | ||
125 | command = malloc(len + 1); | ||
126 | if (!command) | ||
127 | errExit("malloc"); | ||
128 | memset(command, 0, len + 1); | ||
129 | allocated = 1; | ||
130 | |||
131 | // copy | ||
132 | for (i = start_index; i < argc; i++) { | ||
133 | strcat(command, argv[i]); | ||
134 | strcat(command, " "); | ||
135 | } | 266 | } |
136 | } | 267 | } |
137 | 268 | ||
138 | // start the program in /bin/sh | 269 | if (!arg_shell_none) { |
139 | fprintf(stderr, "Warning: an existing sandbox was detected. " | 270 | if (prog_index == 0) { |
140 | "%s will run without any additional sandboxing features in a /bin/sh shell\n", command); | 271 | cfg.command_line = cfg.shell; |
141 | int rv = system(command); | 272 | cfg.window_title = cfg.shell; |
142 | (void) rv; | 273 | } else { |
143 | if (allocated) | 274 | build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index); |
144 | free(command); | 275 | } |
145 | exit(1); | 276 | } |
277 | |||
278 | cfg.original_argv = argv; | ||
279 | cfg.original_program_index = prog_index; | ||
280 | |||
281 | char *command; | ||
282 | if (prog_index == 0) | ||
283 | command = cfg.shell; | ||
284 | else | ||
285 | command = argv[prog_index]; | ||
286 | if (!arg_quiet) | ||
287 | fprintf(stderr, "Warning: an existing sandbox was detected. " | ||
288 | "%s will run without any additional sandboxing features\n", command); | ||
289 | |||
290 | start_application(); | ||
146 | } | 291 | } |
diff --git a/src/firejail/output.c b/src/firejail/output.c index 269ac25ea..91fe7f164 100644 --- a/src/firejail/output.c +++ b/src/firejail/output.c | |||
@@ -27,7 +27,6 @@ void check_output(int argc, char **argv) { | |||
27 | 27 | ||
28 | int i; | 28 | int i; |
29 | char *outfile = NULL; | 29 | char *outfile = NULL; |
30 | // drop_privs(0); | ||
31 | 30 | ||
32 | int found = 0; | 31 | int found = 0; |
33 | for (i = 1; i < argc; i++) { | 32 | for (i = 1; i < argc; i++) { |
@@ -91,6 +90,7 @@ void check_output(int argc, char **argv) { | |||
91 | sprintf(ptr, "2>&1 | %s/firejail/ftee %s", LIBDIR, outfile); | 90 | sprintf(ptr, "2>&1 | %s/firejail/ftee %s", LIBDIR, outfile); |
92 | 91 | ||
93 | // run command | 92 | // run command |
93 | drop_privs(0); | ||
94 | char *a[4]; | 94 | char *a[4]; |
95 | a[0] = "/bin/bash"; | 95 | a[0] = "/bin/bash"; |
96 | a[1] = "-c"; | 96 | a[1] = "-c"; |
diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c new file mode 100644 index 000000000..ea4e6743f --- /dev/null +++ b/src/firejail/preproc.c | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "firejail.h" | ||
21 | #include <sys/mount.h> | ||
22 | #include <sys/stat.h> | ||
23 | |||
24 | static int tmpfs_mounted = 0; | ||
25 | |||
26 | // build /run/firejail directory | ||
27 | void preproc_build_firejail_dir(void) { | ||
28 | struct stat s; | ||
29 | |||
30 | // CentOS 6 doesn't have /run directory | ||
31 | if (stat(RUN_FIREJAIL_BASEDIR, &s)) { | ||
32 | create_empty_dir_as_root(RUN_FIREJAIL_BASEDIR, 0755); | ||
33 | } | ||
34 | |||
35 | if (stat(RUN_FIREJAIL_DIR, &s)) { | ||
36 | create_empty_dir_as_root(RUN_FIREJAIL_DIR, 0755); | ||
37 | } | ||
38 | |||
39 | if (stat(RUN_FIREJAIL_NETWORK_DIR, &s)) { | ||
40 | create_empty_dir_as_root(RUN_FIREJAIL_NETWORK_DIR, 0755); | ||
41 | } | ||
42 | |||
43 | if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s)) { | ||
44 | create_empty_dir_as_root(RUN_FIREJAIL_BANDWIDTH_DIR, 0755); | ||
45 | } | ||
46 | |||
47 | if (stat(RUN_FIREJAIL_NAME_DIR, &s)) { | ||
48 | create_empty_dir_as_root(RUN_FIREJAIL_NAME_DIR, 0755); | ||
49 | } | ||
50 | |||
51 | if (stat(RUN_FIREJAIL_X11_DIR, &s)) { | ||
52 | create_empty_dir_as_root(RUN_FIREJAIL_X11_DIR, 0755); | ||
53 | } | ||
54 | |||
55 | if (stat(RUN_FIREJAIL_APPIMAGE_DIR, &s)) { | ||
56 | create_empty_dir_as_root(RUN_FIREJAIL_APPIMAGE_DIR, 0755); | ||
57 | } | ||
58 | |||
59 | if (stat(RUN_MNT_DIR, &s)) { | ||
60 | create_empty_dir_as_root(RUN_MNT_DIR, 0755); | ||
61 | } | ||
62 | |||
63 | create_empty_file_as_root(RUN_RO_FILE, S_IRUSR); | ||
64 | create_empty_dir_as_root(RUN_RO_DIR, S_IRUSR); | ||
65 | } | ||
66 | |||
67 | // build /run/firejail/mnt directory | ||
68 | void preproc_mount_mnt_dir(void) { | ||
69 | // mount tmpfs on top of /run/firejail/mnt | ||
70 | if (!tmpfs_mounted) { | ||
71 | if (arg_debug) | ||
72 | printf("Mounting tmpfs on %s directory\n", RUN_MNT_DIR); | ||
73 | if (mount("tmpfs", RUN_MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
74 | errExit("mounting /run/firejail/mnt"); | ||
75 | tmpfs_mounted = 1; | ||
76 | fs_logger2("tmpfs", RUN_MNT_DIR); | ||
77 | |||
78 | // create all seccomp files | ||
79 | // as root, create RUN_SECCOMP_I386 file | ||
80 | create_empty_file_as_root(RUN_SECCOMP_I386, 0644); | ||
81 | if (set_perms(RUN_SECCOMP_I386, getuid(), getgid(), 0644)) | ||
82 | errExit("set_perms"); | ||
83 | |||
84 | // as root, create RUN_SECCOMP_AMD64 file | ||
85 | create_empty_file_as_root(RUN_SECCOMP_AMD64, 0644); | ||
86 | if (set_perms(RUN_SECCOMP_AMD64, getuid(), getgid(), 0644)) | ||
87 | errExit("set_perms"); | ||
88 | |||
89 | // as root, create RUN_SECCOMP file | ||
90 | create_empty_file_as_root(RUN_SECCOMP_CFG, 0644); | ||
91 | if (set_perms(RUN_SECCOMP_CFG, getuid(), getgid(), 0644)) | ||
92 | errExit("set_perms"); | ||
93 | |||
94 | // as root, create RUN_SECCOMP_PROTOCOL file | ||
95 | create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); | ||
96 | if (set_perms(RUN_SECCOMP_PROTOCOL, getuid(), getgid(), 0644)) | ||
97 | errExit("set_perms"); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | // grab a copy of cp command | ||
102 | void preproc_build_cp_command(void) { | ||
103 | struct stat s; | ||
104 | preproc_mount_mnt_dir(); | ||
105 | if (stat(RUN_CP_COMMAND, &s)) { | ||
106 | char* fname = realpath("/bin/cp", NULL); | ||
107 | if (fname == NULL || stat(fname, &s) || is_link(fname)) { | ||
108 | fprintf(stderr, "Error: invalid /bin/cp\n"); | ||
109 | exit(1); | ||
110 | } | ||
111 | int rv = copy_file(fname, RUN_CP_COMMAND, 0, 0, 0755); | ||
112 | if (rv) { | ||
113 | fprintf(stderr, "Error: cannot access /bin/cp\n"); | ||
114 | exit(1); | ||
115 | } | ||
116 | ASSERT_PERMS(RUN_CP_COMMAND, 0, 0, 0755); | ||
117 | |||
118 | free(fname); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | // delete the temporary cp command | ||
123 | void preproc_delete_cp_command(void) { | ||
124 | unlink(RUN_CP_COMMAND); | ||
125 | } | ||
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 6ded0ca2f..0fd45d1ef 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -105,7 +105,12 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
105 | // mkdir | 105 | // mkdir |
106 | if (strncmp(ptr, "mkdir ", 6) == 0) { | 106 | if (strncmp(ptr, "mkdir ", 6) == 0) { |
107 | fs_mkdir(ptr + 6); | 107 | fs_mkdir(ptr + 6); |
108 | return 0; | 108 | return 1; // process mkdir again while applying blacklists |
109 | } | ||
110 | // mkfile | ||
111 | if (strncmp(ptr, "mkfile ", 7) == 0) { | ||
112 | fs_mkfile(ptr + 7); | ||
113 | return 1; // process mkfile again while applying blacklists | ||
109 | } | 114 | } |
110 | // sandbox name | 115 | // sandbox name |
111 | else if (strncmp(ptr, "name ", 5) == 0) { | 116 | else if (strncmp(ptr, "name ", 5) == 0) { |
@@ -131,6 +136,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
131 | 136 | ||
132 | return 0; | 137 | return 0; |
133 | } | 138 | } |
139 | else if (strcmp(ptr, "nonewprivs") == 0) { | ||
140 | arg_nonewprivs = 1; | ||
141 | return 0; | ||
142 | } | ||
134 | else if (strcmp(ptr, "seccomp") == 0) { | 143 | else if (strcmp(ptr, "seccomp") == 0) { |
135 | #ifdef HAVE_SECCOMP | 144 | #ifdef HAVE_SECCOMP |
136 | if (checkcfg(CFG_SECCOMP)) | 145 | if (checkcfg(CFG_SECCOMP)) |
@@ -160,6 +169,22 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
160 | arg_private = 1; | 169 | arg_private = 1; |
161 | return 0; | 170 | return 0; |
162 | } | 171 | } |
172 | if (strncmp(ptr, "private-home ", 13) == 0) { | ||
173 | #ifdef HAVE_PRIVATE_HOME | ||
174 | if (checkcfg(CFG_PRIVATE_HOME)) { | ||
175 | cfg.home_private_keep = ptr + 13; | ||
176 | fs_check_home_list(); | ||
177 | arg_private = 1; | ||
178 | } | ||
179 | else | ||
180 | fprintf(stderr, "Warning: private-home is disabled in Firejail configuration file\n"); | ||
181 | #endif | ||
182 | return 0; | ||
183 | } | ||
184 | else if (strcmp(ptr, "allusers") == 0) { | ||
185 | arg_allusers = 1; | ||
186 | return 0; | ||
187 | } | ||
163 | else if (strcmp(ptr, "private-dev") == 0) { | 188 | else if (strcmp(ptr, "private-dev") == 0) { |
164 | arg_private_dev = 1; | 189 | arg_private_dev = 1; |
165 | return 0; | 190 | return 0; |
@@ -174,7 +199,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
174 | } | 199 | } |
175 | else if (strcmp(ptr, "nosound") == 0) { | 200 | else if (strcmp(ptr, "nosound") == 0) { |
176 | arg_nosound = 1; | 201 | arg_nosound = 1; |
177 | arg_private_dev = 1; | 202 | return 0; |
203 | } | ||
204 | else if (strcmp(ptr, "no3d") == 0) { | ||
205 | arg_no3d = 1; | ||
178 | return 0; | 206 | return 0; |
179 | } | 207 | } |
180 | else if (strcmp(ptr, "netfilter") == 0) { | 208 | else if (strcmp(ptr, "netfilter") == 0) { |
@@ -274,6 +302,29 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
274 | return 0; | 302 | return 0; |
275 | } | 303 | } |
276 | 304 | ||
305 | else if (strncmp(ptr, "veth-name ", 10) == 0) { | ||
306 | #ifdef HAVE_NETWORK | ||
307 | if (checkcfg(CFG_NETWORK)) { | ||
308 | Bridge *br = last_bridge_configured(); | ||
309 | if (br == NULL) { | ||
310 | fprintf(stderr, "Error: no network device configured\n"); | ||
311 | exit(1); | ||
312 | } | ||
313 | |||
314 | br->veth_name = strdup(ptr + 10); | ||
315 | if (br->veth_name == NULL) | ||
316 | errExit("strdup"); | ||
317 | if (*br->veth_name == '\0') { | ||
318 | fprintf(stderr, "Error: no veth-name configured\n"); | ||
319 | exit(1); | ||
320 | } | ||
321 | } | ||
322 | else | ||
323 | fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); | ||
324 | #endif | ||
325 | return 0; | ||
326 | } | ||
327 | |||
277 | else if (strncmp(ptr, "iprange ", 8) == 0) { | 328 | else if (strncmp(ptr, "iprange ", 8) == 0) { |
278 | #ifdef HAVE_NETWORK | 329 | #ifdef HAVE_NETWORK |
279 | if (checkcfg(CFG_NETWORK)) { | 330 | if (checkcfg(CFG_NETWORK)) { |
@@ -319,11 +370,145 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
319 | return 0; | 370 | return 0; |
320 | } | 371 | } |
321 | 372 | ||
322 | 373 | ||
374 | else if (strncmp(ptr, "mac ", 4) == 0) { | ||
375 | #ifdef HAVE_NETWORK | ||
376 | if (checkcfg(CFG_NETWORK)) { | ||
377 | Bridge *br = last_bridge_configured(); | ||
378 | if (br == NULL) { | ||
379 | fprintf(stderr, "Error: no network device configured\n"); | ||
380 | exit(1); | ||
381 | } | ||
382 | |||
383 | if (mac_not_zero(br->macsandbox)) { | ||
384 | fprintf(stderr, "Error: cannot configure the MAC address twice for the same interface\n"); | ||
385 | exit(1); | ||
386 | } | ||
387 | |||
388 | // read the address | ||
389 | if (atomac(ptr + 4, br->macsandbox)) { | ||
390 | fprintf(stderr, "Error: invalid MAC address\n"); | ||
391 | exit(1); | ||
392 | } | ||
393 | } | ||
394 | else | ||
395 | fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); | ||
396 | #endif | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | else if (strncmp(ptr, "mtu ", 4) == 0) { | ||
401 | #ifdef HAVE_NETWORK | ||
402 | if (checkcfg(CFG_NETWORK)) { | ||
403 | Bridge *br = last_bridge_configured(); | ||
404 | if (br == NULL) { | ||
405 | fprintf(stderr, "Error: no network device configured\n"); | ||
406 | exit(1); | ||
407 | } | ||
408 | |||
409 | if (sscanf(ptr + 4, "%d", &br->mtu) != 1 || br->mtu < 576 || br->mtu > 9198) { | ||
410 | fprintf(stderr, "Error: invalid mtu value\n"); | ||
411 | exit(1); | ||
412 | } | ||
413 | } | ||
414 | else | ||
415 | fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); | ||
416 | #endif | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | else if (strncmp(ptr, "ip ", 3) == 0) { | ||
421 | #ifdef HAVE_NETWORK | ||
422 | if (checkcfg(CFG_NETWORK)) { | ||
423 | Bridge *br = last_bridge_configured(); | ||
424 | if (br == NULL) { | ||
425 | fprintf(stderr, "Error: no network device configured\n"); | ||
426 | exit(1); | ||
427 | } | ||
428 | if (br->arg_ip_none || br->ipsandbox) { | ||
429 | fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); | ||
430 | exit(1); | ||
431 | } | ||
432 | |||
433 | // configure this IP address for the last bridge defined | ||
434 | if (strcmp(ptr + 3, "none") == 0) | ||
435 | br->arg_ip_none = 1; | ||
436 | else { | ||
437 | if (atoip(ptr + 3, &br->ipsandbox)) { | ||
438 | fprintf(stderr, "Error: invalid IP address\n"); | ||
439 | exit(1); | ||
440 | } | ||
441 | } | ||
442 | } | ||
443 | else | ||
444 | fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); | ||
445 | #endif | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | else if (strncmp(ptr, "ip6 ", 4) == 0) { | ||
450 | #ifdef HAVE_NETWORK | ||
451 | if (checkcfg(CFG_NETWORK)) { | ||
452 | Bridge *br = last_bridge_configured(); | ||
453 | if (br == NULL) { | ||
454 | fprintf(stderr, "Error: no network device configured\n"); | ||
455 | exit(1); | ||
456 | } | ||
457 | if (br->arg_ip_none || br->ip6sandbox) { | ||
458 | fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); | ||
459 | exit(1); | ||
460 | } | ||
461 | |||
462 | // configure this IP address for the last bridge defined | ||
463 | // todo: verify ipv6 syntax | ||
464 | br->ip6sandbox = ptr + 4; | ||
465 | // if (atoip(argv[i] + 5, &br->ipsandbox)) { | ||
466 | // fprintf(stderr, "Error: invalid IP address\n"); | ||
467 | // exit(1); | ||
468 | // } | ||
469 | |||
470 | } | ||
471 | else | ||
472 | fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); | ||
473 | #endif | ||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | else if (strncmp(ptr, "defaultgw ", 10) == 0) { | ||
478 | #ifdef HAVE_NETWORK | ||
479 | if (checkcfg(CFG_NETWORK)) { | ||
480 | if (atoip(ptr + 10, &cfg.defaultgw)) { | ||
481 | fprintf(stderr, "Error: invalid IP address\n"); | ||
482 | exit(1); | ||
483 | } | ||
484 | } | ||
485 | else | ||
486 | fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); | ||
487 | #endif | ||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | if (strcmp(ptr, "apparmor") == 0) { | ||
492 | #ifdef HAVE_APPARMOR | ||
493 | arg_apparmor = 1; | ||
494 | #endif | ||
495 | return 0; | ||
496 | } | ||
497 | |||
323 | if (strncmp(ptr, "protocol ", 9) == 0) { | 498 | if (strncmp(ptr, "protocol ", 9) == 0) { |
324 | #ifdef HAVE_SECCOMP | 499 | #ifdef HAVE_SECCOMP |
325 | if (checkcfg(CFG_SECCOMP)) | 500 | if (checkcfg(CFG_SECCOMP)) { |
326 | protocol_store(ptr + 9); | 501 | if (cfg.protocol) { |
502 | if (!arg_quiet) | ||
503 | fprintf(stderr, "Warning: a protocol list is present, the new list \"%s\" will not be installed\n", ptr + 9); | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | // store list | ||
508 | cfg.protocol = strdup(ptr + 9); | ||
509 | if (!cfg.protocol) | ||
510 | errExit("strdup"); | ||
511 | } | ||
327 | else | 512 | else |
328 | fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); | 513 | fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); |
329 | #endif | 514 | #endif |
@@ -331,7 +516,11 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
331 | } | 516 | } |
332 | 517 | ||
333 | if (strncmp(ptr, "env ", 4) == 0) { | 518 | if (strncmp(ptr, "env ", 4) == 0) { |
334 | env_store(ptr + 4); | 519 | env_store(ptr + 4, SETENV); |
520 | return 0; | ||
521 | } | ||
522 | if (strncmp(ptr, "rmenv ", 6) == 0) { | ||
523 | env_store(ptr + 6, RMENV); | ||
335 | return 0; | 524 | return 0; |
336 | } | 525 | } |
337 | 526 | ||
@@ -340,9 +529,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
340 | #ifdef HAVE_SECCOMP | 529 | #ifdef HAVE_SECCOMP |
341 | if (checkcfg(CFG_SECCOMP)) { | 530 | if (checkcfg(CFG_SECCOMP)) { |
342 | arg_seccomp = 1; | 531 | arg_seccomp = 1; |
343 | cfg.seccomp_list = strdup(ptr + 8); | 532 | cfg.seccomp_list = seccomp_check_list(ptr + 8); |
344 | if (!cfg.seccomp_list) | ||
345 | errExit("strdup"); | ||
346 | } | 533 | } |
347 | else | 534 | else |
348 | fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); | 535 | fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); |
@@ -356,9 +543,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
356 | #ifdef HAVE_SECCOMP | 543 | #ifdef HAVE_SECCOMP |
357 | if (checkcfg(CFG_SECCOMP)) { | 544 | if (checkcfg(CFG_SECCOMP)) { |
358 | arg_seccomp = 1; | 545 | arg_seccomp = 1; |
359 | cfg.seccomp_list_drop = strdup(ptr + 13); | 546 | cfg.seccomp_list_drop = seccomp_check_list(ptr + 13); |
360 | if (!cfg.seccomp_list_drop) | ||
361 | errExit("strdup"); | ||
362 | } | 547 | } |
363 | else | 548 | else |
364 | fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); | 549 | fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); |
@@ -371,9 +556,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
371 | #ifdef HAVE_SECCOMP | 556 | #ifdef HAVE_SECCOMP |
372 | if (checkcfg(CFG_SECCOMP)) { | 557 | if (checkcfg(CFG_SECCOMP)) { |
373 | arg_seccomp = 1; | 558 | arg_seccomp = 1; |
374 | cfg.seccomp_list_keep= strdup(ptr + 13); | 559 | cfg.seccomp_list_keep= seccomp_check_list(ptr + 13); |
375 | if (!cfg.seccomp_list_keep) | ||
376 | errExit("strdup"); | ||
377 | } | 560 | } |
378 | else | 561 | else |
379 | fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); | 562 | fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); |
@@ -387,7 +570,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
387 | arg_caps_list = strdup(ptr + 10); | 570 | arg_caps_list = strdup(ptr + 10); |
388 | if (!arg_caps_list) | 571 | if (!arg_caps_list) |
389 | errExit("strdup"); | 572 | errExit("strdup"); |
390 | // verify seccomp list and exit if problems | 573 | // verify caps list and exit if problems |
391 | if (caps_check_list(arg_caps_list, NULL)) | 574 | if (caps_check_list(arg_caps_list, NULL)) |
392 | exit(1); | 575 | exit(1); |
393 | return 0; | 576 | return 0; |
@@ -399,7 +582,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
399 | arg_caps_list = strdup(ptr + 10); | 582 | arg_caps_list = strdup(ptr + 10); |
400 | if (!arg_caps_list) | 583 | if (!arg_caps_list) |
401 | errExit("strdup"); | 584 | errExit("strdup"); |
402 | // verify seccomp list and exit if problems | 585 | // verify caps list and exit if problems |
403 | if (caps_check_list(arg_caps_list, NULL)) | 586 | if (caps_check_list(arg_caps_list, NULL)) |
404 | exit(1); | 587 | exit(1); |
405 | return 0; | 588 | return 0; |
@@ -441,6 +624,8 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
441 | // nice value | 624 | // nice value |
442 | if (strncmp(ptr, "nice ", 4) == 0) { | 625 | if (strncmp(ptr, "nice ", 4) == 0) { |
443 | cfg.nice = atoi(ptr + 5); | 626 | cfg.nice = atoi(ptr + 5); |
627 | if (getuid() != 0 &&cfg.nice < 0) | ||
628 | cfg.nice = 0; | ||
444 | arg_nice = 1; | 629 | arg_nice = 1; |
445 | return 0; | 630 | return 0; |
446 | } | 631 | } |
@@ -451,6 +636,22 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
451 | return 0; | 636 | return 0; |
452 | } | 637 | } |
453 | 638 | ||
639 | // writable-etc | ||
640 | if (strcmp(ptr, "writable-etc") == 0) { | ||
641 | if (cfg.etc_private_keep) { | ||
642 | fprintf(stderr, "Error: private-etc and writable-etc are mutually exclusive\n"); | ||
643 | exit(1); | ||
644 | } | ||
645 | arg_writable_etc = 1; | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | // writable-var | ||
650 | if (strcmp(ptr, "writable-var") == 0) { | ||
651 | arg_writable_var = 1; | ||
652 | return 0; | ||
653 | } | ||
654 | |||
454 | // private directory | 655 | // private directory |
455 | if (strncmp(ptr, "private ", 8) == 0) { | 656 | if (strncmp(ptr, "private ", 8) == 0) { |
456 | cfg.home_private = ptr + 8; | 657 | cfg.home_private = ptr + 8; |
@@ -459,16 +660,85 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
459 | return 0; | 660 | return 0; |
460 | } | 661 | } |
461 | 662 | ||
663 | if (strcmp(ptr, "x11 none") == 0) { | ||
664 | arg_x11_block = 1; | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | if (strcmp(ptr, "x11 xephyr") == 0) { | ||
669 | #ifdef HAVE_X11 | ||
670 | if (checkcfg(CFG_X11)) { | ||
671 | char *x11env = getenv("FIREJAIL_X11"); | ||
672 | if (x11env && strcmp(x11env, "yes") == 0) { | ||
673 | mask_x11_abstract_socket = 1; | ||
674 | return 0; | ||
675 | } | ||
676 | else { | ||
677 | // start x11 | ||
678 | x11_start_xephyr(cfg.original_argc, cfg.original_argv); | ||
679 | exit(0); | ||
680 | } | ||
681 | } | ||
682 | #endif | ||
683 | return 0; | ||
684 | } | ||
685 | |||
686 | if (strcmp(ptr, "x11 xorg") == 0) { | ||
687 | #ifdef HAVE_X11 | ||
688 | if (checkcfg(CFG_X11)) | ||
689 | arg_x11_xorg = 1; | ||
690 | else { | ||
691 | fprintf(stderr, "Error: --x11 feature is disabled in Firejail configuration file\n"); | ||
692 | return 0; | ||
693 | } | ||
694 | #endif | ||
695 | return 0; | ||
696 | } | ||
697 | if (strcmp(ptr, "x11 xpra") == 0) { | ||
698 | #ifdef HAVE_X11 | ||
699 | if (checkcfg(CFG_X11)) { | ||
700 | char *x11env = getenv("FIREJAIL_X11"); | ||
701 | if (x11env && strcmp(x11env, "yes") == 0) { | ||
702 | mask_x11_abstract_socket = 1; | ||
703 | return 0; | ||
704 | } | ||
705 | else { | ||
706 | // start x11 | ||
707 | x11_start_xpra(cfg.original_argc, cfg.original_argv); | ||
708 | exit(0); | ||
709 | } | ||
710 | } | ||
711 | #endif | ||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | if (strcmp(ptr, "x11") == 0) { | ||
716 | #ifdef HAVE_X11 | ||
717 | if (checkcfg(CFG_X11)) { | ||
718 | char *x11env = getenv("FIREJAIL_X11"); | ||
719 | if (x11env && strcmp(x11env, "yes") == 0) { | ||
720 | mask_x11_abstract_socket = 1; | ||
721 | return 0; | ||
722 | } | ||
723 | else { | ||
724 | // start x11 | ||
725 | x11_start(cfg.original_argc, cfg.original_argv); | ||
726 | exit(0); | ||
727 | } | ||
728 | } | ||
729 | #endif | ||
730 | return 0; | ||
731 | } | ||
732 | |||
462 | // private /etc list of files and directories | 733 | // private /etc list of files and directories |
463 | if (strncmp(ptr, "private-etc ", 12) == 0) { | 734 | if (strncmp(ptr, "private-etc ", 12) == 0) { |
735 | if (arg_writable_etc) { | ||
736 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); | ||
737 | exit(1); | ||
738 | } | ||
464 | cfg.etc_private_keep = ptr + 12; | 739 | cfg.etc_private_keep = ptr + 12; |
465 | fs_check_etc_list(); | 740 | fs_check_etc_list(); |
466 | if (*cfg.etc_private_keep != '\0') | 741 | arg_private_etc = 1; |
467 | arg_private_etc = 1; | ||
468 | else { | ||
469 | arg_private_etc = 0; | ||
470 | fprintf(stderr, "Warning: private-etc disabled, no file found\n"); | ||
471 | } | ||
472 | 742 | ||
473 | return 0; | 743 | return 0; |
474 | } | 744 | } |
@@ -569,6 +839,30 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
569 | return 0; | 839 | return 0; |
570 | } | 840 | } |
571 | 841 | ||
842 | if (strncmp(ptr, "join-or-start ", 14) == 0) { | ||
843 | // try to join by name only | ||
844 | pid_t pid; | ||
845 | if (!name2pid(ptr + 14, &pid)) { | ||
846 | if (!cfg.shell && !arg_shell_none) | ||
847 | cfg.shell = guess_shell(); | ||
848 | |||
849 | // find first non-option arg | ||
850 | int i; | ||
851 | for (i = 1; i < cfg.original_argc && strncmp(cfg.original_argv[i], "--", 2) != 0; i++); | ||
852 | |||
853 | join(pid, cfg.original_argc,cfg.original_argv, i + 1); | ||
854 | exit(0); | ||
855 | } | ||
856 | |||
857 | // set sandbox name and start normally | ||
858 | cfg.name = ptr + 14; | ||
859 | if (strlen(cfg.name) == 0) { | ||
860 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
861 | exit(1); | ||
862 | } | ||
863 | return 0; | ||
864 | } | ||
865 | |||
572 | // rest of filesystem | 866 | // rest of filesystem |
573 | if (strncmp(ptr, "blacklist ", 10) == 0) | 867 | if (strncmp(ptr, "blacklist ", 10) == 0) |
574 | ptr += 10; | 868 | ptr += 10; |
@@ -577,11 +871,23 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
577 | else if (strncmp(ptr, "noblacklist ", 12) == 0) | 871 | else if (strncmp(ptr, "noblacklist ", 12) == 0) |
578 | ptr += 12; | 872 | ptr += 12; |
579 | else if (strncmp(ptr, "whitelist ", 10) == 0) { | 873 | else if (strncmp(ptr, "whitelist ", 10) == 0) { |
580 | arg_whitelist = 1; | 874 | #ifdef HAVE_WHITELIST |
581 | ptr += 10; | 875 | if (checkcfg(CFG_WHITELIST)) { |
876 | arg_whitelist = 1; | ||
877 | ptr += 10; | ||
878 | } | ||
879 | else | ||
880 | return 0; | ||
881 | #else | ||
882 | return 0; | ||
883 | #endif | ||
582 | } | 884 | } |
583 | else if (strncmp(ptr, "read-only ", 10) == 0) | 885 | else if (strncmp(ptr, "read-only ", 10) == 0) |
584 | ptr += 10; | 886 | ptr += 10; |
887 | else if (strncmp(ptr, "read-write ", 11) == 0) | ||
888 | ptr += 11; | ||
889 | else if (strncmp(ptr, "noexec ", 7) == 0) | ||
890 | ptr += 7; | ||
585 | else if (strncmp(ptr, "tmpfs ", 6) == 0) { | 891 | else if (strncmp(ptr, "tmpfs ", 6) == 0) { |
586 | if (getuid() != 0) { | 892 | if (getuid() != 0) { |
587 | fprintf(stderr, "Error: tmpfs available only when running the sandbox as root\n"); | 893 | fprintf(stderr, "Error: tmpfs available only when running the sandbox as root\n"); |
@@ -651,6 +957,16 @@ void profile_read(const char *fname) { | |||
651 | exit(1); | 957 | exit(1); |
652 | } | 958 | } |
653 | 959 | ||
960 | // allow debuggers | ||
961 | if (arg_allow_debuggers) { | ||
962 | char *tmp = strrchr(fname, '/'); | ||
963 | if (tmp && *(tmp + 1) != '\0') { | ||
964 | tmp++; | ||
965 | if (strcmp(tmp, "disable-devel.inc") == 0) | ||
966 | return; | ||
967 | } | ||
968 | } | ||
969 | |||
654 | // open profile file: | 970 | // open profile file: |
655 | FILE *fp = fopen(fname, "r"); | 971 | FILE *fp = fopen(fname, "r"); |
656 | if (fp == NULL) { | 972 | if (fp == NULL) { |
@@ -658,8 +974,7 @@ void profile_read(const char *fname) { | |||
658 | exit(1); | 974 | exit(1); |
659 | } | 975 | } |
660 | 976 | ||
661 | if (!arg_quiet) | 977 | int msg_printed = 0; |
662 | fprintf(stderr, "Reading profile %s\n", fname); | ||
663 | 978 | ||
664 | // read the file line by line | 979 | // read the file line by line |
665 | char buf[MAX_READ + 1]; | 980 | char buf[MAX_READ + 1]; |
@@ -677,6 +992,17 @@ void profile_read(const char *fname) { | |||
677 | continue; | 992 | continue; |
678 | } | 993 | } |
679 | 994 | ||
995 | // process quiet | ||
996 | if (strcmp(ptr, "quiet") == 0) { | ||
997 | arg_quiet = 1; | ||
998 | continue; | ||
999 | } | ||
1000 | if (!msg_printed) { | ||
1001 | if (!arg_quiet) | ||
1002 | fprintf(stderr, "Reading profile %s\n", fname); | ||
1003 | msg_printed = 1; | ||
1004 | } | ||
1005 | |||
680 | // process include | 1006 | // process include |
681 | if (strncmp(ptr, "include ", 8) == 0) { | 1007 | if (strncmp(ptr, "include ", 8) == 0) { |
682 | include_level++; | 1008 | include_level++; |
diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index 7e5ab7dfb..2a09ed010 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c | |||
@@ -18,269 +18,18 @@ | |||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | /* | ||
22 | struct sock_filter filter[] = { | ||
23 | VALIDATE_ARCHITECTURE, | ||
24 | EXAMINE_SYSCALL, | ||
25 | ONLY(SYS_socket), | ||
26 | EXAMINE_ARGUMENT(0), // allow only AF_INET and AF_INET6, drop everything else | ||
27 | WHITELIST(AF_INET), | ||
28 | WHITELIST(AF_INET6), | ||
29 | WHITELIST(AF_PACKET), | ||
30 | RETURN_ERRNO(ENOTSUP) | ||
31 | }; | ||
32 | struct sock_fprog prog = { | ||
33 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | ||
34 | .filter = filter, | ||
35 | }; | ||
36 | |||
37 | |||
38 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
39 | perror("prctl(NO_NEW_PRIVS)"); | ||
40 | return 1; | ||
41 | } | ||
42 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | ||
43 | perror("prctl"); | ||
44 | return 1; | ||
45 | } | ||
46 | */ | ||
47 | |||
48 | #ifdef HAVE_SECCOMP | 21 | #ifdef HAVE_SECCOMP |
49 | #include "firejail.h" | 22 | #include "firejail.h" |
50 | #include "seccomp.h" | 23 | #include "../include/seccomp.h" |
51 | #include <sys/types.h> | ||
52 | #include <sys/socket.h> | ||
53 | |||
54 | static char *protocol[] = { | ||
55 | "unix", | ||
56 | "inet", | ||
57 | "inet6", | ||
58 | "netlink", | ||
59 | "packet", | ||
60 | NULL | ||
61 | }; | ||
62 | |||
63 | static struct sock_filter protocol_filter_command[] = { | ||
64 | WHITELIST(AF_UNIX), | ||
65 | WHITELIST(AF_INET), | ||
66 | WHITELIST(AF_INET6), | ||
67 | WHITELIST(AF_NETLINK), | ||
68 | WHITELIST(AF_PACKET) | ||
69 | }; | ||
70 | // Note: protocol[] and protocol_filter_command are synchronized | ||
71 | |||
72 | // command length | ||
73 | struct sock_filter whitelist[] = { | ||
74 | WHITELIST(AF_UNIX) | ||
75 | }; | ||
76 | unsigned whitelist_len = sizeof(whitelist) / sizeof(struct sock_filter); | ||
77 | |||
78 | |||
79 | |||
80 | static int is_protocol(const char *p) { | ||
81 | int i = 0; | ||
82 | while (protocol[i] != NULL) { | ||
83 | if (strcmp(protocol[i], p) == 0) | ||
84 | return 1; | ||
85 | i++; | ||
86 | } | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static struct sock_filter *find_protocol_domain(const char *p) { | ||
92 | int i = 0; | ||
93 | while (protocol[i] != NULL) { | ||
94 | if (strcmp(protocol[i], p) == 0) | ||
95 | return &protocol_filter_command[i * whitelist_len]; | ||
96 | i++; | ||
97 | } | ||
98 | |||
99 | return NULL; | ||
100 | } | ||
101 | |||
102 | // --debug-protocols | ||
103 | void protocol_list(void) { | ||
104 | EUID_ASSERT(); | ||
105 | |||
106 | #ifndef SYS_socket | ||
107 | fprintf(stderr, "Warning: --protocol not supported on this platform\n"); | ||
108 | return; | ||
109 | #endif | ||
110 | |||
111 | int i = 0; | ||
112 | while (protocol[i] != NULL) { | ||
113 | printf("%s, ", protocol[i]); | ||
114 | i++; | ||
115 | } | ||
116 | printf("\n"); | ||
117 | } | ||
118 | |||
119 | |||
120 | // check protocol list and store it in cfg structure | ||
121 | void protocol_store(const char *prlist) { | ||
122 | EUID_ASSERT(); | ||
123 | assert(prlist); | ||
124 | |||
125 | if (cfg.protocol && !arg_quiet) { | ||
126 | fprintf(stderr, "Warning: a protocol list is present, the new list \"%s\" will not be installed\n", prlist); | ||
127 | return; | ||
128 | } | ||
129 | |||
130 | // temporary list | ||
131 | char *tmplist = strdup(prlist); | ||
132 | if (!tmplist) | ||
133 | errExit("strdup"); | ||
134 | |||
135 | // check list | ||
136 | char *token = strtok(tmplist, ","); | ||
137 | if (!token) | ||
138 | goto errout; | ||
139 | |||
140 | while (token) { | ||
141 | if (!is_protocol(token)) | ||
142 | goto errout; | ||
143 | token = strtok(NULL, ","); | ||
144 | } | ||
145 | free(tmplist); | ||
146 | |||
147 | // store list | ||
148 | cfg.protocol = strdup(prlist); | ||
149 | if (!cfg.protocol) | ||
150 | errExit("strdup"); | ||
151 | return; | ||
152 | |||
153 | errout: | ||
154 | fprintf(stderr, "Error: invalid protocol list\n"); | ||
155 | exit(1); | ||
156 | } | ||
157 | |||
158 | // install protocol filter | ||
159 | void protocol_filter(void) { | ||
160 | assert(cfg.protocol); | ||
161 | if (arg_debug) | ||
162 | printf("Set protocol filter: %s\n", cfg.protocol); | ||
163 | |||
164 | #ifndef SYS_socket | ||
165 | (void) find_protocol_domain; | ||
166 | fprintf(stderr, "Warning: --protocol not supported on this platform\n"); | ||
167 | return; | ||
168 | #else | ||
169 | // build the filter | ||
170 | struct sock_filter filter[32]; // big enough | ||
171 | memset(&filter[0], 0, sizeof(filter)); | ||
172 | uint8_t *ptr = (uint8_t *) &filter[0]; | ||
173 | |||
174 | // header | ||
175 | struct sock_filter filter_start[] = { | ||
176 | VALIDATE_ARCHITECTURE, | ||
177 | EXAMINE_SYSCALL, | ||
178 | ONLY(SYS_socket), | ||
179 | EXAMINE_ARGUMENT(0) | ||
180 | }; | ||
181 | memcpy(ptr, &filter_start[0], sizeof(filter_start)); | ||
182 | ptr += sizeof(filter_start); | ||
183 | |||
184 | #if 0 | ||
185 | printf("entries %u\n", (unsigned) (sizeof(filter_start) / sizeof(struct sock_filter))); | ||
186 | { | ||
187 | unsigned j; | ||
188 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
189 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
190 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
191 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
192 | printf("%02x, ", (*ptr2) & 0xff); | ||
193 | } | ||
194 | printf("\n"); | ||
195 | } | ||
196 | printf("whitelist_len %u, struct sock_filter len %u\n", whitelist_len, (unsigned) sizeof(struct sock_filter)); | ||
197 | #endif | ||
198 | |||
199 | |||
200 | // parse list and add commands | ||
201 | char *tmplist = strdup(cfg.protocol); | ||
202 | if (!tmplist) | ||
203 | errExit("strdup"); | ||
204 | char *token = strtok(tmplist, ","); | ||
205 | if (!token) | ||
206 | errExit("strtok"); | ||
207 | |||
208 | while (token) { | ||
209 | struct sock_filter *domain = find_protocol_domain(token); | ||
210 | assert(domain); | ||
211 | memcpy(ptr, domain, whitelist_len * sizeof(struct sock_filter)); | ||
212 | ptr += whitelist_len * sizeof(struct sock_filter); | ||
213 | token = strtok(NULL, ","); | ||
214 | |||
215 | #if 0 | ||
216 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
217 | { | ||
218 | unsigned j; | ||
219 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
220 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
221 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
222 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
223 | printf("%02x, ", (*ptr2) & 0xff); | ||
224 | } | ||
225 | printf("\n"); | ||
226 | } | ||
227 | #endif | ||
228 | |||
229 | |||
230 | } | ||
231 | free(tmplist); | ||
232 | |||
233 | // add end of filter | ||
234 | struct sock_filter filter_end[] = { | ||
235 | RETURN_ERRNO(ENOTSUP) | ||
236 | }; | ||
237 | memcpy(ptr, &filter_end[0], sizeof(filter_end)); | ||
238 | ptr += sizeof(filter_end); | ||
239 | |||
240 | #if 0 | ||
241 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
242 | { | ||
243 | unsigned j; | ||
244 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
245 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
246 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
247 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
248 | printf("%02x, ", (*ptr2) & 0xff); | ||
249 | } | ||
250 | printf("\n"); | ||
251 | } | ||
252 | #endif | ||
253 | |||
254 | // install filter | ||
255 | unsigned short entries = (unsigned short) ((uintptr_t) ptr - (uintptr_t) (filter)) / (unsigned) sizeof(struct sock_filter); | ||
256 | struct sock_fprog prog = { | ||
257 | .len = entries, | ||
258 | .filter = filter, | ||
259 | }; | ||
260 | |||
261 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
262 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | ||
263 | return; | ||
264 | } | ||
265 | #endif // SYS_socket | ||
266 | } | ||
267 | 24 | ||
268 | void protocol_filter_save(void) { | 25 | void protocol_filter_save(void) { |
269 | // save protocol filter configuration in PROTOCOL_CFG | 26 | // save protocol filter configuration in PROTOCOL_CFG |
270 | fs_build_mnt_dir(); | ||
271 | |||
272 | FILE *fp = fopen(RUN_PROTOCOL_CFG, "w"); | 27 | FILE *fp = fopen(RUN_PROTOCOL_CFG, "w"); |
273 | if (!fp) | 28 | if (!fp) |
274 | errExit("fopen"); | 29 | errExit("fopen"); |
275 | fprintf(fp, "%s\n", cfg.protocol); | 30 | fprintf(fp, "%s\n", cfg.protocol); |
31 | SET_PERMS_STREAM(fp, 0, 0, 0600); | ||
276 | fclose(fp); | 32 | fclose(fp); |
277 | |||
278 | if (chmod(RUN_PROTOCOL_CFG, 0600) < 0) | ||
279 | errExit("chmod"); | ||
280 | |||
281 | if (chown(RUN_PROTOCOL_CFG, 0, 0) < 0) | ||
282 | errExit("chown"); | ||
283 | |||
284 | } | 33 | } |
285 | 34 | ||
286 | void protocol_filter_load(const char *fname) { | 35 | void protocol_filter_load(const char *fname) { |
@@ -310,29 +59,6 @@ void protocol_filter_load(const char *fname) { | |||
310 | 59 | ||
311 | 60 | ||
312 | // --protocol.print | 61 | // --protocol.print |
313 | void protocol_print_filter_name(const char *name) { | ||
314 | EUID_ASSERT(); | ||
315 | |||
316 | (void) name; | ||
317 | #ifdef SYS_socket | ||
318 | if (!name || strlen(name) == 0) { | ||
319 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
320 | exit(1); | ||
321 | } | ||
322 | pid_t pid; | ||
323 | if (name2pid(name, &pid)) { | ||
324 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
325 | exit(1); | ||
326 | } | ||
327 | |||
328 | protocol_print_filter(pid); | ||
329 | #else | ||
330 | fprintf(stderr, "Warning: --protocol not supported on this platform\n"); | ||
331 | return; | ||
332 | #endif | ||
333 | } | ||
334 | |||
335 | // --protocol.print | ||
336 | void protocol_print_filter(pid_t pid) { | 62 | void protocol_print_filter(pid_t pid) { |
337 | EUID_ASSERT(); | 63 | EUID_ASSERT(); |
338 | 64 | ||
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c index 1eb5e59e1..6ec590eaa 100644 --- a/src/firejail/pulseaudio.c +++ b/src/firejail/pulseaudio.c | |||
@@ -53,16 +53,32 @@ doexit: | |||
53 | 53 | ||
54 | // disable pulseaudio socket | 54 | // disable pulseaudio socket |
55 | void pulseaudio_disable(void) { | 55 | void pulseaudio_disable(void) { |
56 | if (arg_debug) | ||
57 | printf("disable pulseaudio\n"); | ||
56 | // blacklist user config directory | 58 | // blacklist user config directory |
57 | disable_file(cfg.homedir, ".config/pulse"); | 59 | disable_file(cfg.homedir, ".config/pulse"); |
58 | 60 | ||
61 | |||
62 | // blacklist pulseaudio socket in XDG_RUNTIME_DIR | ||
63 | char *name = getenv("XDG_RUNTIME_DIR"); | ||
64 | if (name) | ||
65 | disable_file(name, "pulse/native"); | ||
66 | |||
67 | // try the default location anyway | ||
68 | char *path; | ||
69 | if (asprintf(&path, "/run/user/%d", getuid()) == -1) | ||
70 | errExit("asprintf"); | ||
71 | disable_file(path, "pulse/native"); | ||
72 | free(path); | ||
73 | |||
74 | |||
75 | |||
59 | // blacklist any pulse* file in /tmp directory | 76 | // blacklist any pulse* file in /tmp directory |
60 | DIR *dir; | 77 | DIR *dir; |
61 | if (!(dir = opendir("/tmp"))) { | 78 | if (!(dir = opendir("/tmp"))) { |
62 | // sleep 2 seconds and try again | 79 | // sleep 2 seconds and try again |
63 | sleep(2); | 80 | sleep(2); |
64 | if (!(dir = opendir("/tmp"))) { | 81 | if (!(dir = opendir("/tmp"))) { |
65 | fprintf(stderr, "Warning: cannot open /tmp directory. PulseAudio sockets are not disabled\n"); | ||
66 | return; | 82 | return; |
67 | } | 83 | } |
68 | } | 84 | } |
@@ -76,10 +92,6 @@ void pulseaudio_disable(void) { | |||
76 | 92 | ||
77 | closedir(dir); | 93 | closedir(dir); |
78 | 94 | ||
79 | // blacklist XDG_RUNTIME_DIR | ||
80 | char *name = getenv("XDG_RUNTIME_DIR"); | ||
81 | if (name) | ||
82 | disable_file(name, "pulse/native"); | ||
83 | } | 95 | } |
84 | 96 | ||
85 | 97 | ||
@@ -92,33 +104,23 @@ void pulseaudio_init(void) { | |||
92 | return; | 104 | return; |
93 | 105 | ||
94 | // create the new user pulseaudio directory | 106 | // create the new user pulseaudio directory |
95 | fs_build_mnt_dir(); | ||
96 | int rv = mkdir(RUN_PULSE_DIR, 0700); | 107 | int rv = mkdir(RUN_PULSE_DIR, 0700); |
97 | (void) rv; // in --chroot mode the directory can already be there | 108 | (void) rv; // in --chroot mode the directory can already be there |
98 | if (chown(RUN_PULSE_DIR, getuid(), getgid()) < 0) | 109 | if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) |
99 | errExit("chown"); | 110 | errExit("set_perms"); |
100 | if (chmod(RUN_PULSE_DIR, 0700) < 0) | ||
101 | errExit("chmod"); | ||
102 | 111 | ||
103 | // create the new client.conf file | 112 | // create the new client.conf file |
104 | char *pulsecfg = NULL; | 113 | char *pulsecfg = NULL; |
105 | if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1) | 114 | if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1) |
106 | errExit("asprintf"); | 115 | errExit("asprintf"); |
107 | if (is_link("/etc/pulse/client.conf")) { | 116 | if (copy_file("/etc/pulse/client.conf", pulsecfg, -1, -1, 0644)) |
108 | fprintf(stderr, "Error: invalid /etc/pulse/client.conf file\n"); | ||
109 | exit(1); | ||
110 | } | ||
111 | if (copy_file("/etc/pulse/client.conf", pulsecfg)) | ||
112 | errExit("copy_file"); | 117 | errExit("copy_file"); |
113 | FILE *fp = fopen(pulsecfg, "a+"); | 118 | FILE *fp = fopen(pulsecfg, "a+"); |
114 | if (!fp) | 119 | if (!fp) |
115 | errExit("fopen"); | 120 | errExit("fopen"); |
116 | fprintf(fp, "%s", "\nenable-shm = no\n"); | 121 | fprintf(fp, "%s", "\nenable-shm = no\n"); |
122 | SET_PERMS_STREAM(fp, getuid(), getgid(), 0644); | ||
117 | fclose(fp); | 123 | fclose(fp); |
118 | if (chmod(pulsecfg, 0644) == -1) | ||
119 | errExit("chmod"); | ||
120 | if (chown(pulsecfg, getuid(), getgid()) == -1) | ||
121 | errExit("chown"); | ||
122 | 124 | ||
123 | // create ~/.config/pulse directory if not present | 125 | // create ~/.config/pulse directory if not present |
124 | char *dir1; | 126 | char *dir1; |
@@ -127,10 +129,8 @@ void pulseaudio_init(void) { | |||
127 | if (stat(dir1, &s) == -1) { | 129 | if (stat(dir1, &s) == -1) { |
128 | int rv = mkdir(dir1, 0755); | 130 | int rv = mkdir(dir1, 0755); |
129 | if (rv == 0) { | 131 | if (rv == 0) { |
130 | rv = chown(dir1, getuid(), getgid()); | 132 | if (set_perms(dir1, getuid(), getgid(), 0755)) |
131 | (void) rv; | 133 | {;} // do nothing |
132 | rv = chmod(dir1, 0755); | ||
133 | (void) rv; | ||
134 | } | 134 | } |
135 | } | 135 | } |
136 | free(dir1); | 136 | free(dir1); |
@@ -139,10 +139,8 @@ void pulseaudio_init(void) { | |||
139 | if (stat(dir1, &s) == -1) { | 139 | if (stat(dir1, &s) == -1) { |
140 | int rv = mkdir(dir1, 0700); | 140 | int rv = mkdir(dir1, 0700); |
141 | if (rv == 0) { | 141 | if (rv == 0) { |
142 | rv = chown(dir1, getuid(), getgid()); | 142 | if (set_perms(dir1, getuid(), getgid(), 0700)) |
143 | (void) rv; | 143 | {;} // do nothing |
144 | rv = chmod(dir1, 0700); | ||
145 | (void) rv; | ||
146 | } | 144 | } |
147 | } | 145 | } |
148 | free(dir1); | 146 | free(dir1); |
diff --git a/src/firejail/restrict_users.c b/src/firejail/restrict_users.c index 5a41c441b..393851148 100644 --- a/src/firejail/restrict_users.c +++ b/src/firejail/restrict_users.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <dirent.h> | 26 | #include <dirent.h> |
27 | #include <fcntl.h> | 27 | #include <fcntl.h> |
28 | #include <errno.h> | 28 | #include <errno.h> |
29 | #include "../../uids.h" | ||
29 | 30 | ||
30 | #define MAXBUF 1024 | 31 | #define MAXBUF 1024 |
31 | 32 | ||
@@ -72,7 +73,6 @@ static void sanitize_home(void) { | |||
72 | return; | 73 | return; |
73 | } | 74 | } |
74 | 75 | ||
75 | fs_build_mnt_dir(); | ||
76 | if (mkdir(RUN_WHITELIST_HOME_DIR, 0755) == -1) | 76 | if (mkdir(RUN_WHITELIST_HOME_DIR, 0755) == -1) |
77 | errExit("mkdir"); | 77 | errExit("mkdir"); |
78 | 78 | ||
@@ -95,10 +95,8 @@ static void sanitize_home(void) { | |||
95 | fs_logger2("mkdir", cfg.homedir); | 95 | fs_logger2("mkdir", cfg.homedir); |
96 | 96 | ||
97 | // set mode and ownership | 97 | // set mode and ownership |
98 | if (chown(cfg.homedir, s.st_uid, s.st_gid) == -1) | 98 | if (set_perms(cfg.homedir, s.st_uid, s.st_gid, s.st_mode)) |
99 | errExit("chown"); | 99 | errExit("set_perms"); |
100 | if (chmod(cfg.homedir, s.st_mode) == -1) | ||
101 | errExit("chmod"); | ||
102 | 100 | ||
103 | // mount user home directory | 101 | // mount user home directory |
104 | if (mount(RUN_WHITELIST_HOME_DIR, cfg.homedir, NULL, MS_BIND|MS_REC, NULL) < 0) | 102 | if (mount(RUN_WHITELIST_HOME_DIR, cfg.homedir, NULL, MS_BIND|MS_REC, NULL) < 0) |
@@ -118,7 +116,7 @@ static void sanitize_passwd(void) { | |||
118 | if (stat("/etc/passwd", &s) == -1) | 116 | if (stat("/etc/passwd", &s) == -1) |
119 | return; | 117 | return; |
120 | if (arg_debug) | 118 | if (arg_debug) |
121 | printf("Sanitizing /etc/passwd\n"); | 119 | printf("Sanitizing /etc/passwd, UID_MIN %d\n", UID_MIN); |
122 | if (is_link("/etc/passwd")) { | 120 | if (is_link("/etc/passwd")) { |
123 | fprintf(stderr, "Error: invalid /etc/passwd\n"); | 121 | fprintf(stderr, "Error: invalid /etc/passwd\n"); |
124 | exit(1); | 122 | exit(1); |
@@ -126,7 +124,6 @@ static void sanitize_passwd(void) { | |||
126 | 124 | ||
127 | FILE *fpin = NULL; | 125 | FILE *fpin = NULL; |
128 | FILE *fpout = NULL; | 126 | FILE *fpout = NULL; |
129 | fs_build_mnt_dir(); | ||
130 | 127 | ||
131 | // open files | 128 | // open files |
132 | /* coverity[toctou] */ | 129 | /* coverity[toctou] */ |
@@ -170,7 +167,7 @@ static void sanitize_passwd(void) { | |||
170 | int rv = sscanf(ptr, "%d:", &uid); | 167 | int rv = sscanf(ptr, "%d:", &uid); |
171 | if (rv == 0 || uid < 0) | 168 | if (rv == 0 || uid < 0) |
172 | goto errout; | 169 | goto errout; |
173 | if (uid < 1000) { // todo extract UID_MIN from /etc/login.def | 170 | if (uid < UID_MIN) { |
174 | fprintf(fpout, "%s", buf); | 171 | fprintf(fpout, "%s", buf); |
175 | continue; | 172 | continue; |
176 | } | 173 | } |
@@ -186,12 +183,9 @@ static void sanitize_passwd(void) { | |||
186 | fprintf(fpout, "%s", buf); | 183 | fprintf(fpout, "%s", buf); |
187 | } | 184 | } |
188 | fclose(fpin); | 185 | fclose(fpin); |
186 | SET_PERMS_STREAM(fpout, 0, 0, 0644); | ||
189 | fclose(fpout); | 187 | fclose(fpout); |
190 | if (chown(RUN_PASSWD_FILE, 0, 0) == -1) | 188 | |
191 | errExit("chown"); | ||
192 | if (chmod(RUN_PASSWD_FILE, 0644) == -1) | ||
193 | errExit("chmod"); | ||
194 | |||
195 | // mount-bind tne new password file | 189 | // mount-bind tne new password file |
196 | if (mount(RUN_PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0) | 190 | if (mount(RUN_PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0) |
197 | errExit("mount"); | 191 | errExit("mount"); |
@@ -255,7 +249,7 @@ static void sanitize_group(void) { | |||
255 | if (stat("/etc/group", &s) == -1) | 249 | if (stat("/etc/group", &s) == -1) |
256 | return; | 250 | return; |
257 | if (arg_debug) | 251 | if (arg_debug) |
258 | printf("Sanitizing /etc/group\n"); | 252 | printf("Sanitizing /etc/group, GID_MIN %d\n", GID_MIN); |
259 | if (is_link("/etc/group")) { | 253 | if (is_link("/etc/group")) { |
260 | fprintf(stderr, "Error: invalid /etc/group\n"); | 254 | fprintf(stderr, "Error: invalid /etc/group\n"); |
261 | exit(1); | 255 | exit(1); |
@@ -263,7 +257,6 @@ static void sanitize_group(void) { | |||
263 | 257 | ||
264 | FILE *fpin = NULL; | 258 | FILE *fpin = NULL; |
265 | FILE *fpout = NULL; | 259 | FILE *fpout = NULL; |
266 | fs_build_mnt_dir(); | ||
267 | 260 | ||
268 | // open files | 261 | // open files |
269 | /* coverity[toctou] */ | 262 | /* coverity[toctou] */ |
@@ -306,7 +299,7 @@ static void sanitize_group(void) { | |||
306 | int rv = sscanf(ptr, "%d:", &gid); | 299 | int rv = sscanf(ptr, "%d:", &gid); |
307 | if (rv == 0 || gid < 0) | 300 | if (rv == 0 || gid < 0) |
308 | goto errout; | 301 | goto errout; |
309 | if (gid < 1000) { // todo extract GID_MIN from /etc/login.def | 302 | if (gid < GID_MIN) { |
310 | if (copy_line(fpout, buf, ptr)) | 303 | if (copy_line(fpout, buf, ptr)) |
311 | goto errout; | 304 | goto errout; |
312 | continue; | 305 | continue; |
@@ -318,12 +311,9 @@ static void sanitize_group(void) { | |||
318 | goto errout; | 311 | goto errout; |
319 | } | 312 | } |
320 | fclose(fpin); | 313 | fclose(fpin); |
314 | SET_PERMS_STREAM(fpout, 0, 0, 0644); | ||
321 | fclose(fpout); | 315 | fclose(fpout); |
322 | if (chown(RUN_GROUP_FILE, 0, 0) == -1) | 316 | |
323 | errExit("chown"); | ||
324 | if (chmod(RUN_GROUP_FILE, 0644) == -1) | ||
325 | errExit("chmod"); | ||
326 | |||
327 | // mount-bind tne new group file | 317 | // mount-bind tne new group file |
328 | if (mount(RUN_GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0) | 318 | if (mount(RUN_GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0) |
329 | errExit("mount"); | 319 | errExit("mount"); |
@@ -340,6 +330,9 @@ errout: | |||
340 | } | 330 | } |
341 | 331 | ||
342 | void restrict_users(void) { | 332 | void restrict_users(void) { |
333 | if (arg_allusers) | ||
334 | return; | ||
335 | |||
343 | // only in user mode | 336 | // only in user mode |
344 | if (getuid()) { | 337 | if (getuid()) { |
345 | if (strncmp(cfg.homedir, "/home/", 6) == 0) { | 338 | if (strncmp(cfg.homedir, "/home/", 6) == 0) { |
@@ -347,7 +340,7 @@ void restrict_users(void) { | |||
347 | sanitize_home(); | 340 | sanitize_home(); |
348 | } | 341 | } |
349 | else { | 342 | else { |
350 | // user has the home diercotry outside /home | 343 | // user has the home directory outside /home |
351 | // mount tmpfs on top of /home in order to hide it | 344 | // mount tmpfs on top of /home in order to hide it |
352 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 345 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
353 | errExit("mount tmpfs"); | 346 | errExit("mount tmpfs"); |
diff --git a/src/firejail/restricted_shell.c b/src/firejail/restricted_shell.c index ee6e94957..979bb1eed 100644 --- a/src/firejail/restricted_shell.c +++ b/src/firejail/restricted_shell.c | |||
@@ -18,6 +18,7 @@ | |||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | #include "firejail.h" | 20 | #include "firejail.h" |
21 | #include <fnmatch.h> | ||
21 | 22 | ||
22 | #define MAX_READ 4096 // maximum line length | 23 | #define MAX_READ 4096 // maximum line length |
23 | char *restricted_user = NULL; | 24 | char *restricted_user = NULL; |
@@ -40,7 +41,7 @@ int restricted_shell(const char *user) { | |||
40 | char buf[MAX_READ]; | 41 | char buf[MAX_READ]; |
41 | while (fgets(buf, MAX_READ, fp)) { | 42 | while (fgets(buf, MAX_READ, fp)) { |
42 | lineno++; | 43 | lineno++; |
43 | 44 | ||
44 | // remove empty spaces at the beginning of the line | 45 | // remove empty spaces at the beginning of the line |
45 | char *ptr = buf; | 46 | char *ptr = buf; |
46 | while (*ptr == ' ' || *ptr == '\t') { | 47 | while (*ptr == ' ' || *ptr == '\t') { |
@@ -48,21 +49,26 @@ int restricted_shell(const char *user) { | |||
48 | } | 49 | } |
49 | if (*ptr == '\n' || *ptr == '#') | 50 | if (*ptr == '\n' || *ptr == '#') |
50 | continue; | 51 | continue; |
52 | |||
53 | // | ||
54 | // parse line | ||
55 | // | ||
51 | 56 | ||
52 | // parse line | 57 | // extract users |
53 | char *usr = ptr; | 58 | char *usr = ptr; |
54 | char *args = strchr(usr, ':'); | 59 | char *args = strchr(usr, ':'); |
55 | if (args == NULL) { | 60 | if (args == NULL) { |
56 | fprintf(stderr, "Error: users.conf line %d\n", lineno); | 61 | fprintf(stderr, "Error: users.conf line %d\n", lineno); |
57 | exit(1); | 62 | exit(1); |
58 | } | 63 | } |
64 | |||
59 | *args = '\0'; | 65 | *args = '\0'; |
60 | args++; | 66 | args++; |
61 | ptr = strchr(args, '\n'); | 67 | ptr = strchr(args, '\n'); |
62 | if (ptr) | 68 | if (ptr) |
63 | *ptr = '\0'; | 69 | *ptr = '\0'; |
64 | 70 | ||
65 | // if nothing follows, continue | 71 | // extract firejail command line arguments |
66 | char *ptr2 = args; | 72 | char *ptr2 = args; |
67 | int found = 0; | 73 | int found = 0; |
68 | while (*ptr2 != '\0') { | 74 | while (*ptr2 != '\0') { |
@@ -70,29 +76,42 @@ int restricted_shell(const char *user) { | |||
70 | found = 1; | 76 | found = 1; |
71 | break; | 77 | break; |
72 | } | 78 | } |
79 | ptr2++; | ||
73 | } | 80 | } |
81 | // if nothing follows, continue | ||
74 | if (!found) | 82 | if (!found) |
75 | continue; | 83 | continue; |
76 | 84 | ||
77 | // process user | 85 | // user name globbing |
78 | if (strcmp(user, usr) == 0) { | 86 | if (fnmatch(usr, user, 0) == 0) { |
79 | restricted_user = strdup(user); | 87 | // process program arguments |
80 | // extract program arguments | ||
81 | 88 | ||
82 | fullargv[0] = "firejail"; | 89 | fullargv[0] = "firejail"; |
83 | int i; | 90 | int i; |
84 | ptr = args; | 91 | ptr = args; |
85 | for (i = 1; i < MAX_ARGS; i++) { | 92 | for (i = 1; i < MAX_ARGS; i++) { |
86 | fullargv[i] = ptr; | 93 | // skip blanks |
87 | while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') | 94 | while (*ptr == ' ' || *ptr == '\t') |
88 | ptr++; | 95 | ptr++; |
96 | fullargv[i] = ptr; | ||
97 | #ifdef DEBUG_RESTRICTED_SHELL | ||
98 | {EUID_ROOT(); | ||
99 | FILE *fp = fopen("/firelog", "a"); | ||
100 | if (fp) { | ||
101 | fprintf(fp, "i %d ptr #%s#\n", i, fullargv[i]); | ||
102 | fclose(fp); | ||
103 | } | ||
104 | EUID_USER();} | ||
105 | #endif | ||
106 | |||
89 | if (*ptr != '\0') { | 107 | if (*ptr != '\0') { |
108 | // go to the end of the word | ||
109 | while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') | ||
110 | ptr++; | ||
90 | *ptr ='\0'; | 111 | *ptr ='\0'; |
91 | fullargv[i] = strdup(fullargv[i]); | 112 | fullargv[i] = strdup(fullargv[i]); |
92 | if (fullargv[i] == NULL) { | 113 | if (fullargv[i] == NULL) |
93 | fprintf(stderr, "Error: cannot allocate memory\n"); | 114 | errExit("strdup"); |
94 | exit(1); | ||
95 | } | ||
96 | ptr++; | 115 | ptr++; |
97 | while (*ptr == ' ' || *ptr == '\t') | 116 | while (*ptr == ' ' || *ptr == '\t') |
98 | ptr++; | 117 | ptr++; |
@@ -108,7 +127,7 @@ int restricted_shell(const char *user) { | |||
108 | } | 127 | } |
109 | } | 128 | } |
110 | fclose(fp); | 129 | fclose(fp); |
111 | 130 | ||
112 | return 0; | 131 | return 0; |
113 | } | 132 | } |
114 | 133 | ||
diff --git a/src/firejail/run_symlink.c b/src/firejail/run_symlink.c index d57816e12..8aa2fe53f 100644 --- a/src/firejail/run_symlink.c +++ b/src/firejail/run_symlink.c | |||
@@ -91,14 +91,22 @@ void run_symlink(int argc, char **argv) { | |||
91 | 91 | ||
92 | printf("Redirecting symlink to %s\n", program); | 92 | printf("Redirecting symlink to %s\n", program); |
93 | 93 | ||
94 | // drop privileges | ||
95 | if (setgid(getgid()) < 0) | ||
96 | errExit("setgid/getgid"); | ||
97 | if (setuid(getuid()) < 0) | ||
98 | errExit("setuid/getuid"); | ||
99 | |||
94 | // run command | 100 | // run command |
95 | char *a[3 + argc]; | 101 | char *a[3 + argc]; |
96 | a[0] = firejail; | 102 | a[0] = firejail; |
97 | a[1] = program; | 103 | a[1] = program; |
98 | int i; | 104 | int i; |
99 | for (i = 0; i < (argc - 1); i++) | 105 | for (i = 0; i < (argc - 1); i++) { |
100 | a[i + 2] = argv[i + 1]; | 106 | a[i + 2] = argv[i + 1]; |
107 | } | ||
101 | a[i + 2] = NULL; | 108 | a[i + 2] = NULL; |
109 | assert(getenv("LD_PRELOAD") == NULL); | ||
102 | execvp(a[0], a); | 110 | execvp(a[0], a); |
103 | 111 | ||
104 | perror("execvp"); | 112 | perror("execvp"); |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 3f3564295..109daf552 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -28,12 +28,23 @@ | |||
28 | #include <sys/types.h> | 28 | #include <sys/types.h> |
29 | #include <dirent.h> | 29 | #include <dirent.h> |
30 | #include <errno.h> | 30 | #include <errno.h> |
31 | #include <fcntl.h> | ||
31 | 32 | ||
32 | #include <sched.h> | 33 | #include <sched.h> |
33 | #ifndef CLONE_NEWUSER | 34 | #ifndef CLONE_NEWUSER |
34 | #define CLONE_NEWUSER 0x10000000 | 35 | #define CLONE_NEWUSER 0x10000000 |
35 | #endif | 36 | #endif |
36 | 37 | ||
38 | #include <sys/prctl.h> | ||
39 | #ifndef PR_SET_NO_NEW_PRIVS | ||
40 | # define PR_SET_NO_NEW_PRIVS 38 | ||
41 | #endif | ||
42 | |||
43 | #ifdef HAVE_APPARMOR | ||
44 | #include <sys/apparmor.h> | ||
45 | #endif | ||
46 | |||
47 | |||
37 | static int monitored_pid = 0; | 48 | static int monitored_pid = 0; |
38 | static void sandbox_handler(int sig){ | 49 | static void sandbox_handler(int sig){ |
39 | if (!arg_quiet) { | 50 | if (!arg_quiet) { |
@@ -70,8 +81,11 @@ static void sandbox_handler(int sig){ | |||
70 | 81 | ||
71 | } | 82 | } |
72 | 83 | ||
84 | |||
73 | // broadcast a SIGKILL | 85 | // broadcast a SIGKILL |
74 | kill(-1, SIGKILL); | 86 | kill(-1, SIGKILL); |
87 | flush_stdin(); | ||
88 | |||
75 | exit(sig); | 89 | exit(sig); |
76 | } | 90 | } |
77 | 91 | ||
@@ -94,9 +108,8 @@ void save_nogroups(void) { | |||
94 | FILE *fp = fopen(RUN_GROUPS_CFG, "w"); | 108 | FILE *fp = fopen(RUN_GROUPS_CFG, "w"); |
95 | if (fp) { | 109 | if (fp) { |
96 | fprintf(fp, "\n"); | 110 | fprintf(fp, "\n"); |
111 | SET_PERMS_STREAM(fp, 0, 0, 0644); // assume mode 0644 | ||
97 | fclose(fp); | 112 | fclose(fp); |
98 | if (chown(RUN_GROUPS_CFG, 0, 0) < 0) | ||
99 | errExit("chown"); | ||
100 | } | 113 | } |
101 | else { | 114 | else { |
102 | fprintf(stderr, "Error: cannot save nogroups state\n"); | 115 | fprintf(stderr, "Error: cannot save nogroups state\n"); |
@@ -109,7 +122,7 @@ static void sandbox_if_up(Bridge *br) { | |||
109 | assert(br); | 122 | assert(br); |
110 | if (!br->configured) | 123 | if (!br->configured) |
111 | return; | 124 | return; |
112 | 125 | ||
113 | char *dev = br->devsandbox; | 126 | char *dev = br->devsandbox; |
114 | net_if_up(dev); | 127 | net_if_up(dev); |
115 | 128 | ||
@@ -124,8 +137,7 @@ static void sandbox_if_up(Bridge *br) { | |||
124 | assert(br->ipsandbox); | 137 | assert(br->ipsandbox); |
125 | if (arg_debug) | 138 | if (arg_debug) |
126 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); | 139 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); |
127 | net_if_ip(dev, br->ipsandbox, br->mask, br->mtu); | 140 | net_config_interface(dev, br->ipsandbox, br->mask, br->mtu); |
128 | net_if_up(dev); | ||
129 | } | 141 | } |
130 | else if (br->arg_ip_none == 0 && br->macvlan == 1) { | 142 | else if (br->arg_ip_none == 0 && br->macvlan == 1) { |
131 | // reassign the macvlan address | 143 | // reassign the macvlan address |
@@ -147,8 +159,7 @@ static void sandbox_if_up(Bridge *br) { | |||
147 | 159 | ||
148 | if (arg_debug) | 160 | if (arg_debug) |
149 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); | 161 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); |
150 | net_if_ip(dev, br->ipsandbox, br->mask, br->mtu); | 162 | net_config_interface(dev, br->ipsandbox, br->mask, br->mtu); |
151 | net_if_up(dev); | ||
152 | } | 163 | } |
153 | 164 | ||
154 | if (br->ip6sandbox) | 165 | if (br->ip6sandbox) |
@@ -198,6 +209,12 @@ static int monitor_application(pid_t app_pid) { | |||
198 | if (arg_debug) | 209 | if (arg_debug) |
199 | printf("Sandbox monitor: waitpid %u retval %d status %d\n", monitored_pid, rv, status); | 210 | printf("Sandbox monitor: waitpid %u retval %d status %d\n", monitored_pid, rv, status); |
200 | 211 | ||
212 | // if /proc is not remounted, we cannot check /proc directory, | ||
213 | // for now we just get out of here | ||
214 | // todo: find another way of checking child processes! | ||
215 | if (!checkcfg(CFG_REMOUNT_PROC_SYS)) | ||
216 | break; | ||
217 | |||
201 | DIR *dir; | 218 | DIR *dir; |
202 | if (!(dir = opendir("/proc"))) { | 219 | if (!(dir = opendir("/proc"))) { |
203 | // sleep 2 seconds and try again | 220 | // sleep 2 seconds and try again |
@@ -237,40 +254,46 @@ static int monitor_application(pid_t app_pid) { | |||
237 | 254 | ||
238 | // return the latest exit status. | 255 | // return the latest exit status. |
239 | return status; | 256 | return status; |
257 | } | ||
240 | 258 | ||
241 | #if 0 | 259 | void start_audit(void) { |
242 | // todo: find a way to shut down interfaces before closing the namespace | 260 | char *audit_prog; |
243 | // the problem is we don't have enough privileges to shutdown interfaces in this moment | 261 | if (asprintf(&audit_prog, "%s/firejail/faudit", LIBDIR) == -1) |
244 | // shut down bridge/macvlan interfaces | 262 | errExit("asprintf"); |
245 | if (any_bridge_configured()) { | 263 | assert(getenv("LD_PRELOAD") == NULL); |
246 | 264 | execl(audit_prog, audit_prog, NULL); | |
247 | if (cfg.bridge0.configured) { | 265 | perror("execl"); |
248 | printf("Shutting down %s\n", cfg.bridge0.devsandbox); | 266 | exit(1); |
249 | net_if_down( cfg.bridge0.devsandbox); | ||
250 | } | ||
251 | if (cfg.bridge1.configured) { | ||
252 | printf("Shutting down %s\n", cfg.bridge1.devsandbox); | ||
253 | net_if_down( cfg.bridge1.devsandbox); | ||
254 | } | ||
255 | if (cfg.bridge2.configured) { | ||
256 | printf("Shutting down %s\n", cfg.bridge2.devsandbox); | ||
257 | net_if_down( cfg.bridge2.devsandbox); | ||
258 | } | ||
259 | if (cfg.bridge3.configured) { | ||
260 | printf("Shutting down %s\n", cfg.bridge3.devsandbox); | ||
261 | net_if_down( cfg.bridge3.devsandbox); | ||
262 | } | ||
263 | usleep(20000); // 20 ms sleep | ||
264 | } | ||
265 | #endif | ||
266 | } | 267 | } |
267 | 268 | ||
269 | void start_application(void) { | ||
270 | //if (setsid() == -1) | ||
271 | //errExit("setsid"); | ||
268 | 272 | ||
269 | static void start_application(void) { | 273 | // set environment |
274 | env_defaults(); | ||
275 | env_apply(); | ||
276 | if (arg_debug) { | ||
277 | printf("starting application\n"); | ||
278 | printf("LD_PRELOAD=%s\n", getenv("LD_PRELOAD")); | ||
279 | } | ||
280 | |||
281 | //**************************************** | ||
282 | // audit | ||
283 | //**************************************** | ||
284 | if (arg_audit) { | ||
285 | assert(arg_audit_prog); | ||
286 | struct stat s; | ||
287 | if (stat(arg_audit_prog, &s) != 0) { | ||
288 | fprintf(stderr, "Error: cannot find the audit program\n"); | ||
289 | exit(1); | ||
290 | } | ||
291 | execl(arg_audit_prog, arg_audit_prog, NULL); | ||
292 | } | ||
270 | //**************************************** | 293 | //**************************************** |
271 | // start the program without using a shell | 294 | // start the program without using a shell |
272 | //**************************************** | 295 | //**************************************** |
273 | if (arg_shell_none) { | 296 | else if (arg_shell_none) { |
274 | if (arg_debug) { | 297 | if (arg_debug) { |
275 | int i; | 298 | int i; |
276 | for (i = cfg.original_program_index; i < cfg.original_argc; i++) { | 299 | for (i = cfg.original_program_index; i < cfg.original_argc; i++) { |
@@ -289,35 +312,33 @@ static void start_application(void) { | |||
289 | printf("Child process initialized\n"); | 312 | printf("Child process initialized\n"); |
290 | 313 | ||
291 | execvp(cfg.original_argv[cfg.original_program_index], &cfg.original_argv[cfg.original_program_index]); | 314 | execvp(cfg.original_argv[cfg.original_program_index], &cfg.original_argv[cfg.original_program_index]); |
315 | exit(1); | ||
292 | } | 316 | } |
293 | //**************************************** | 317 | //**************************************** |
294 | // start the program using a shell | 318 | // start the program using a shell |
295 | //**************************************** | 319 | //**************************************** |
296 | else { | 320 | else { |
297 | // choose the shell requested by the user, or use bash as default | 321 | assert(cfg.shell); |
298 | char *sh; | 322 | assert(cfg.command_line); |
299 | if (cfg.shell) | 323 | |
300 | sh = cfg.shell; | ||
301 | else if (arg_zsh) | ||
302 | sh = "/usr/bin/zsh"; | ||
303 | else if (arg_csh) | ||
304 | sh = "/bin/csh"; | ||
305 | else | ||
306 | sh = "/bin/bash"; | ||
307 | |||
308 | char *arg[5]; | 324 | char *arg[5]; |
309 | int index = 0; | 325 | int index = 0; |
310 | arg[index++] = sh; | 326 | arg[index++] = cfg.shell; |
311 | arg[index++] = "-c"; | 327 | if (login_shell) { |
312 | assert(cfg.command_line); | 328 | arg[index++] = "-l"; |
313 | if (arg_debug) | 329 | if (arg_debug) |
314 | printf("Starting %s\n", cfg.command_line); | 330 | printf("Starting %s login shell\n", cfg.shell); |
315 | if (arg_doubledash) | 331 | } else { |
316 | arg[index++] = "--"; | 332 | arg[index++] = "-c"; |
317 | arg[index++] = cfg.command_line; | 333 | if (arg_debug) |
334 | printf("Running %s command through %s\n", cfg.command_line, cfg.shell); | ||
335 | if (arg_doubledash) | ||
336 | arg[index++] = "--"; | ||
337 | arg[index++] = cfg.command_line; | ||
338 | } | ||
318 | arg[index] = NULL; | 339 | arg[index] = NULL; |
319 | assert(index < 5); | 340 | assert(index < 5); |
320 | 341 | ||
321 | if (arg_debug) { | 342 | if (arg_debug) { |
322 | char *msg; | 343 | char *msg; |
323 | if (asprintf(&msg, "sandbox %d, execvp into %s", sandbox_pid, cfg.command_line) == -1) | 344 | if (asprintf(&msg, "sandbox %d, execvp into %s", sandbox_pid, cfg.command_line) == -1) |
@@ -325,7 +346,7 @@ static void start_application(void) { | |||
325 | logmsg(msg); | 346 | logmsg(msg); |
326 | free(msg); | 347 | free(msg); |
327 | } | 348 | } |
328 | 349 | ||
329 | if (arg_debug) { | 350 | if (arg_debug) { |
330 | int i; | 351 | int i; |
331 | for (i = 0; i < 5; i++) { | 352 | for (i = 0; i < 5; i++) { |
@@ -334,17 +355,40 @@ static void start_application(void) { | |||
334 | printf("execvp argument %d: %s\n", i, arg[i]); | 355 | printf("execvp argument %d: %s\n", i, arg[i]); |
335 | } | 356 | } |
336 | } | 357 | } |
337 | 358 | ||
338 | if (!arg_command && !arg_quiet) | 359 | if (!arg_command && !arg_quiet) |
339 | printf("Child process initialized\n"); | 360 | printf("Child process initialized\n"); |
340 | execvp(sh, arg); | 361 | execvp(arg[0], arg); |
341 | } | 362 | } |
342 | 363 | ||
343 | perror("execvp"); | 364 | perror("execvp"); |
344 | exit(1); // it should never get here!!! | 365 | exit(1); // it should never get here!!! |
345 | } | 366 | } |
346 | 367 | ||
347 | 368 | static void enforce_filters(void) { | |
369 | // force default seccomp inside the chroot, no keep or drop list | ||
370 | // the list build on top of the default drop list is kept intact | ||
371 | arg_seccomp = 1; | ||
372 | if (cfg.seccomp_list_drop) { | ||
373 | free(cfg.seccomp_list_drop); | ||
374 | cfg.seccomp_list_drop = NULL; | ||
375 | } | ||
376 | if (cfg.seccomp_list_keep) { | ||
377 | free(cfg.seccomp_list_keep); | ||
378 | cfg.seccomp_list_keep = NULL; | ||
379 | } | ||
380 | |||
381 | // disable all capabilities | ||
382 | if (arg_caps_default_filter || arg_caps_list) | ||
383 | fprintf(stderr, "Warning: all capabilities disabled for a regular user in chroot\n"); | ||
384 | arg_caps_drop_all = 1; | ||
385 | |||
386 | // drop all supplementary groups; /etc/group file inside chroot | ||
387 | // is controlled by a regular usr | ||
388 | arg_nogroups = 1; | ||
389 | if (!arg_quiet) | ||
390 | printf("Dropping all Linux capabilities and enforcing default seccomp filter\n"); | ||
391 | } | ||
348 | 392 | ||
349 | int sandbox(void* sandbox_arg) { | 393 | int sandbox(void* sandbox_arg) { |
350 | // Get rid of unused parameter warning | 394 | // Get rid of unused parameter warning |
@@ -364,6 +408,7 @@ int sandbox(void* sandbox_arg) { | |||
364 | if (arg_debug && child_pid == 1) | 408 | if (arg_debug && child_pid == 1) |
365 | printf("PID namespace installed\n"); | 409 | printf("PID namespace installed\n"); |
366 | 410 | ||
411 | |||
367 | //**************************** | 412 | //**************************** |
368 | // set hostname | 413 | // set hostname |
369 | //**************************** | 414 | //**************************** |
@@ -379,7 +424,8 @@ int sandbox(void* sandbox_arg) { | |||
379 | if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { | 424 | if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { |
380 | chk_chroot(); | 425 | chk_chroot(); |
381 | } | 426 | } |
382 | 427 | // ... and mount a tmpfs on top of /run/firejail/mnt directory | |
428 | preproc_mount_mnt_dir(); | ||
383 | 429 | ||
384 | //**************************** | 430 | //**************************** |
385 | // log sandbox data | 431 | // log sandbox data |
@@ -396,7 +442,7 @@ int sandbox(void* sandbox_arg) { | |||
396 | fs_logger("install mount namespace"); | 442 | fs_logger("install mount namespace"); |
397 | 443 | ||
398 | //**************************** | 444 | //**************************** |
399 | // netfilter etc. | 445 | // netfilter |
400 | //**************************** | 446 | //**************************** |
401 | if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter | 447 | if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter |
402 | netfilter(arg_netfilter_file); | 448 | netfilter(arg_netfilter_file); |
@@ -405,6 +451,101 @@ int sandbox(void* sandbox_arg) { | |||
405 | netfilter6(arg_netfilter6_file); | 451 | netfilter6(arg_netfilter6_file); |
406 | } | 452 | } |
407 | 453 | ||
454 | //**************************** | ||
455 | // networking | ||
456 | //**************************** | ||
457 | int gw_cfg_failed = 0; // default gw configuration flag | ||
458 | if (arg_nonetwork) { | ||
459 | net_if_up("lo"); | ||
460 | if (arg_debug) | ||
461 | printf("Network namespace enabled, only loopback interface available\n"); | ||
462 | } | ||
463 | else if (any_bridge_configured() || any_interface_configured()) { | ||
464 | // configure lo and eth0...eth3 | ||
465 | net_if_up("lo"); | ||
466 | |||
467 | if (mac_not_zero(cfg.bridge0.macsandbox)) | ||
468 | net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); | ||
469 | sandbox_if_up(&cfg.bridge0); | ||
470 | |||
471 | if (mac_not_zero(cfg.bridge1.macsandbox)) | ||
472 | net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); | ||
473 | sandbox_if_up(&cfg.bridge1); | ||
474 | |||
475 | if (mac_not_zero(cfg.bridge2.macsandbox)) | ||
476 | net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); | ||
477 | sandbox_if_up(&cfg.bridge2); | ||
478 | |||
479 | if (mac_not_zero(cfg.bridge3.macsandbox)) | ||
480 | net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); | ||
481 | sandbox_if_up(&cfg.bridge3); | ||
482 | |||
483 | |||
484 | // moving an interface in a namespace using --interface will reset the interface configuration; | ||
485 | // we need to put the configuration back | ||
486 | if (cfg.interface0.configured && cfg.interface0.ip) { | ||
487 | if (arg_debug) | ||
488 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); | ||
489 | net_config_interface(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu); | ||
490 | } | ||
491 | if (cfg.interface1.configured && cfg.interface1.ip) { | ||
492 | if (arg_debug) | ||
493 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); | ||
494 | net_config_interface(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu); | ||
495 | } | ||
496 | if (cfg.interface2.configured && cfg.interface2.ip) { | ||
497 | if (arg_debug) | ||
498 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); | ||
499 | net_config_interface(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu); | ||
500 | } | ||
501 | if (cfg.interface3.configured && cfg.interface3.ip) { | ||
502 | if (arg_debug) | ||
503 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); | ||
504 | net_config_interface(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu); | ||
505 | } | ||
506 | |||
507 | // add a default route | ||
508 | if (cfg.defaultgw) { | ||
509 | // set the default route | ||
510 | if (net_add_route(0, 0, cfg.defaultgw)) { | ||
511 | fprintf(stderr, "Warning: cannot configure default route\n"); | ||
512 | gw_cfg_failed = 1; | ||
513 | } | ||
514 | } | ||
515 | |||
516 | if (arg_debug) | ||
517 | printf("Network namespace enabled\n"); | ||
518 | } | ||
519 | |||
520 | |||
521 | // print network configuration | ||
522 | if (!arg_quiet) { | ||
523 | if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { | ||
524 | printf("\n"); | ||
525 | if (any_bridge_configured() || any_interface_configured()) { | ||
526 | // net_ifprint(); | ||
527 | if (arg_scan) | ||
528 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "printif", "scan"); | ||
529 | else | ||
530 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 2, PATH_FNET, "printif", "scan"); | ||
531 | |||
532 | } | ||
533 | if (cfg.defaultgw != 0) { | ||
534 | if (gw_cfg_failed) | ||
535 | printf("Default gateway configuration failed\n"); | ||
536 | else | ||
537 | printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); | ||
538 | } | ||
539 | if (cfg.dns1 != 0) | ||
540 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); | ||
541 | if (cfg.dns2 != 0) | ||
542 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); | ||
543 | if (cfg.dns3 != 0) | ||
544 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); | ||
545 | printf("\n"); | ||
546 | } | ||
547 | } | ||
548 | |||
408 | // load IBUS env variables | 549 | // load IBUS env variables |
409 | if (arg_nonetwork || any_bridge_configured() || any_interface_configured()) { | 550 | if (arg_nonetwork || any_bridge_configured() || any_interface_configured()) { |
410 | // do nothing - there are problems with ibus version 1.5.11 | 551 | // do nothing - there are problems with ibus version 1.5.11 |
@@ -412,11 +553,29 @@ int sandbox(void* sandbox_arg) { | |||
412 | else | 553 | else |
413 | env_ibus_load(); | 554 | env_ibus_load(); |
414 | 555 | ||
415 | // grab a copy of cp command | 556 | //**************************** |
416 | fs_build_cp_command(); | 557 | // fs pre-processing: |
417 | 558 | // - copy some commands under /run | |
559 | // - build seccomp filters | ||
560 | // - create an empty /etc/ld.so.preload | ||
561 | //**************************** | ||
562 | preproc_build_cp_command(); | ||
563 | |||
564 | #ifdef HAVE_SECCOMP | ||
565 | if (cfg.protocol) { | ||
566 | if (arg_debug) | ||
567 | printf("Build protocol filter: %s\n", cfg.protocol); | ||
568 | |||
569 | // build the seccomp filter as a regular user | ||
570 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, | ||
571 | PATH_FSECCOMP, "protocol", "build", cfg.protocol, RUN_SECCOMP_PROTOCOL); | ||
572 | if (rv) | ||
573 | exit(rv); | ||
574 | } | ||
575 | #endif | ||
576 | |||
418 | // trace pre-install | 577 | // trace pre-install |
419 | if (arg_trace || arg_tracelog) | 578 | if (arg_trace || arg_tracelog || mask_x11_abstract_socket) |
420 | fs_trace_preload(); | 579 | fs_trace_preload(); |
421 | 580 | ||
422 | //**************************** | 581 | //**************************** |
@@ -425,39 +584,23 @@ int sandbox(void* sandbox_arg) { | |||
425 | #ifdef HAVE_SECCOMP | 584 | #ifdef HAVE_SECCOMP |
426 | int enforce_seccomp = 0; | 585 | int enforce_seccomp = 0; |
427 | #endif | 586 | #endif |
587 | if (arg_appimage) { | ||
588 | enforce_filters(); | ||
589 | #ifdef HAVE_SECCOMP | ||
590 | enforce_seccomp = 1; | ||
591 | #endif | ||
592 | } | ||
593 | |||
428 | #ifdef HAVE_CHROOT | 594 | #ifdef HAVE_CHROOT |
429 | if (cfg.chrootdir) { | 595 | if (cfg.chrootdir) { |
430 | fs_chroot(cfg.chrootdir); | 596 | fs_chroot(cfg.chrootdir); |
431 | // redo cp command | ||
432 | fs_build_cp_command(); | ||
433 | 597 | ||
434 | // force caps and seccomp if not started as root | 598 | // force caps and seccomp if not started as root |
435 | if (getuid() != 0) { | 599 | if (getuid() != 0) { |
436 | // force default seccomp inside the chroot, no keep or drop list | 600 | enforce_filters(); |
437 | // the list build on top of the default drop list is kept intact | ||
438 | arg_seccomp = 1; | ||
439 | #ifdef HAVE_SECCOMP | 601 | #ifdef HAVE_SECCOMP |
440 | enforce_seccomp = 1; | 602 | enforce_seccomp = 1; |
441 | #endif | 603 | #endif |
442 | if (cfg.seccomp_list_drop) { | ||
443 | free(cfg.seccomp_list_drop); | ||
444 | cfg.seccomp_list_drop = NULL; | ||
445 | } | ||
446 | if (cfg.seccomp_list_keep) { | ||
447 | free(cfg.seccomp_list_keep); | ||
448 | cfg.seccomp_list_keep = NULL; | ||
449 | } | ||
450 | |||
451 | // disable all capabilities | ||
452 | if (arg_caps_default_filter || arg_caps_list) | ||
453 | fprintf(stderr, "Warning: all capabilities disabled for a regular user during chroot\n"); | ||
454 | arg_caps_drop_all = 1; | ||
455 | |||
456 | // drop all supplementary groups; /etc/group file inside chroot | ||
457 | // is controlled by a regular usr | ||
458 | arg_nogroups = 1; | ||
459 | if (!arg_quiet) | ||
460 | printf("Dropping all Linux capabilities and enforcing default seccomp filter\n"); | ||
461 | } | 604 | } |
462 | else | 605 | else |
463 | arg_seccomp = 1; | 606 | arg_seccomp = 1; |
@@ -465,17 +608,28 @@ int sandbox(void* sandbox_arg) { | |||
465 | //**************************** | 608 | //**************************** |
466 | // trace pre-install, this time inside chroot | 609 | // trace pre-install, this time inside chroot |
467 | //**************************** | 610 | //**************************** |
468 | if (arg_trace || arg_tracelog) | 611 | if (arg_trace || arg_tracelog || mask_x11_abstract_socket) |
469 | fs_trace_preload(); | 612 | fs_trace_preload(); |
470 | } | 613 | } |
471 | else | 614 | else |
472 | #endif | 615 | #endif |
473 | if (arg_overlay) | 616 | #ifdef HAVE_OVERLAYFS |
617 | if (arg_overlay) { | ||
474 | fs_overlayfs(); | 618 | fs_overlayfs(); |
619 | // force caps and seccomp if not started as root | ||
620 | if (getuid() != 0) { | ||
621 | enforce_filters(); | ||
622 | #ifdef HAVE_SECCOMP | ||
623 | enforce_seccomp = 1; | ||
624 | #endif | ||
625 | } | ||
626 | else | ||
627 | arg_seccomp = 1; | ||
628 | } | ||
475 | else | 629 | else |
630 | #endif | ||
476 | fs_basic_fs(); | 631 | fs_basic_fs(); |
477 | 632 | ||
478 | |||
479 | //**************************** | 633 | //**************************** |
480 | // set hostname in /etc/hostname | 634 | // set hostname in /etc/hostname |
481 | //**************************** | 635 | //**************************** |
@@ -487,145 +641,135 @@ int sandbox(void* sandbox_arg) { | |||
487 | // private mode | 641 | // private mode |
488 | //**************************** | 642 | //**************************** |
489 | if (arg_private) { | 643 | if (arg_private) { |
490 | if (cfg.home_private) // --private= | 644 | if (cfg.home_private) { // --private= |
491 | fs_private_homedir(); | 645 | if (cfg.chrootdir) |
646 | fprintf(stderr, "Warning: private=directory feature is disabled in chroot\n"); | ||
647 | else if (arg_overlay) | ||
648 | fprintf(stderr, "Warning: private=directory feature is disabled in overlay\n"); | ||
649 | else | ||
650 | fs_private_homedir(); | ||
651 | } | ||
652 | else if (cfg.home_private_keep) { // --private-home= | ||
653 | if (cfg.chrootdir) | ||
654 | fprintf(stderr, "Warning: private-home= feature is disabled in chroot\n"); | ||
655 | else if (arg_overlay) | ||
656 | fprintf(stderr, "Warning: private-home= feature is disabled in overlay\n"); | ||
657 | else | ||
658 | fs_private_home_list(); | ||
659 | } | ||
492 | else // --private | 660 | else // --private |
493 | fs_private(); | 661 | fs_private(); |
494 | } | 662 | } |
495 | 663 | ||
496 | if (arg_private_dev) | 664 | if (arg_private_dev) { |
497 | fs_private_dev(); | 665 | if (cfg.chrootdir) |
666 | fprintf(stderr, "Warning: private-dev feature is disabled in chroot\n"); | ||
667 | else if (arg_overlay) | ||
668 | fprintf(stderr, "Warning: private-dev feature is disabled in overlay\n"); | ||
669 | else | ||
670 | fs_private_dev(); | ||
671 | } | ||
672 | |||
498 | if (arg_private_etc) { | 673 | if (arg_private_etc) { |
499 | fs_private_etc_list(); | 674 | if (cfg.chrootdir) |
500 | // create /etc/ld.so.preload file again | 675 | fprintf(stderr, "Warning: private-etc feature is disabled in chroot\n"); |
501 | if (arg_trace || arg_tracelog) | 676 | else if (arg_overlay) |
502 | fs_trace_preload(); | 677 | fprintf(stderr, "Warning: private-etc feature is disabled in overlay\n"); |
678 | else { | ||
679 | fs_private_etc_list(); | ||
680 | // create /etc/ld.so.preload file again | ||
681 | if (arg_trace || arg_tracelog || mask_x11_abstract_socket) | ||
682 | fs_trace_preload(); | ||
683 | } | ||
684 | } | ||
685 | |||
686 | if (arg_private_bin) { | ||
687 | if (cfg.chrootdir) | ||
688 | fprintf(stderr, "Warning: private-bin feature is disabled in chroot\n"); | ||
689 | else if (arg_overlay) | ||
690 | fprintf(stderr, "Warning: private-bin feature is disabled in overlay\n"); | ||
691 | else { | ||
692 | // for --x11=xorg we need to add xauth command | ||
693 | if (arg_x11_xorg) { | ||
694 | EUID_USER(); | ||
695 | char *tmp; | ||
696 | if (asprintf(&tmp, "%s,xauth", cfg.bin_private_keep) == -1) | ||
697 | errExit("asprintf"); | ||
698 | cfg.bin_private_keep = tmp; | ||
699 | fs_check_bin_list(); | ||
700 | EUID_ROOT(); | ||
701 | } | ||
702 | fs_private_bin_list(); | ||
703 | } | ||
704 | } | ||
705 | |||
706 | if (arg_private_tmp) { | ||
707 | if (cfg.chrootdir) | ||
708 | fprintf(stderr, "Warning: private-tmp feature is disabled in chroot\n"); | ||
709 | else if (arg_overlay) | ||
710 | fprintf(stderr, "Warning: private-tmp feature is disabled in overlay\n"); | ||
711 | else { | ||
712 | // private-tmp is implemented as a whitelist | ||
713 | EUID_USER(); | ||
714 | profile_add("whitelist /tmp/.X11-unix"); | ||
715 | EUID_ROOT(); | ||
716 | } | ||
503 | } | 717 | } |
504 | if (arg_private_bin) | 718 | |
505 | fs_private_bin_list(); | 719 | //**************************** |
506 | if (arg_private_tmp) | 720 | // update /proc, /sys, /dev, /boot directorymy |
507 | fs_private_tmp(); | 721 | //**************************** |
722 | if (checkcfg(CFG_REMOUNT_PROC_SYS)) | ||
723 | fs_proc_sys_dev_boot(); | ||
508 | 724 | ||
509 | //**************************** | 725 | //**************************** |
510 | // apply the profile file | 726 | // apply the profile file |
511 | //**************************** | 727 | //**************************** |
512 | if (cfg.profile) { | 728 | // apply all whitelist commands ... |
513 | // apply all whitelist commands ... | 729 | if (cfg.chrootdir) |
730 | fprintf(stderr, "Warning: whitelist feature is disabled in chroot\n"); | ||
731 | else if (arg_overlay) | ||
732 | fprintf(stderr, "Warning: whitelist feature is disabled in overlay\n"); | ||
733 | else | ||
514 | fs_whitelist(); | 734 | fs_whitelist(); |
515 | 735 | ||
516 | // ... followed by blacklist commands | 736 | // ... followed by blacklist commands |
517 | fs_blacklist(); | 737 | fs_blacklist(); // mkdir and mkfile are processed all over again |
518 | } | ||
519 | 738 | ||
520 | //**************************** | 739 | //**************************** |
521 | // install trace | 740 | // install trace |
522 | //**************************** | 741 | //**************************** |
523 | if (arg_trace || arg_tracelog) | 742 | if (arg_trace || arg_tracelog || mask_x11_abstract_socket) |
524 | fs_trace(); | 743 | fs_trace(); |
525 | 744 | ||
526 | //**************************** | 745 | //**************************** |
527 | // update /proc, /dev, /boot directorymy | 746 | // nosound/no3d and fix for pulseaudio 7.0 |
528 | //**************************** | ||
529 | fs_proc_sys_dev_boot(); | ||
530 | |||
531 | //**************************** | ||
532 | // --nosound and fix for pulseaudio 7.0 | ||
533 | //**************************** | 747 | //**************************** |
534 | if (arg_nosound) | 748 | if (arg_nosound) { |
749 | // disable pulseaudio | ||
535 | pulseaudio_disable(); | 750 | pulseaudio_disable(); |
751 | |||
752 | // disable /dev/snd | ||
753 | fs_dev_disable_sound(); | ||
754 | } | ||
536 | else | 755 | else |
537 | pulseaudio_init(); | 756 | pulseaudio_init(); |
538 | 757 | ||
758 | if (arg_no3d) | ||
759 | fs_dev_disable_3d(); | ||
760 | |||
539 | //**************************** | 761 | //**************************** |
540 | // networking | 762 | // set dns |
541 | //**************************** | 763 | //**************************** |
542 | if (arg_nonetwork) { | ||
543 | net_if_up("lo"); | ||
544 | if (arg_debug) | ||
545 | printf("Network namespace enabled, only loopback interface available\n"); | ||
546 | } | ||
547 | else if (any_bridge_configured() || any_interface_configured()) { | ||
548 | // configure lo and eth0...eth3 | ||
549 | net_if_up("lo"); | ||
550 | |||
551 | if (mac_not_zero(cfg.bridge0.macsandbox)) | ||
552 | net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); | ||
553 | sandbox_if_up(&cfg.bridge0); | ||
554 | |||
555 | if (mac_not_zero(cfg.bridge1.macsandbox)) | ||
556 | net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); | ||
557 | sandbox_if_up(&cfg.bridge1); | ||
558 | |||
559 | if (mac_not_zero(cfg.bridge2.macsandbox)) | ||
560 | net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); | ||
561 | sandbox_if_up(&cfg.bridge2); | ||
562 | |||
563 | if (mac_not_zero(cfg.bridge3.macsandbox)) | ||
564 | net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); | ||
565 | sandbox_if_up(&cfg.bridge3); | ||
566 | |||
567 | // add a default route | ||
568 | if (cfg.defaultgw) { | ||
569 | // set the default route | ||
570 | if (net_add_route(0, 0, cfg.defaultgw)) | ||
571 | fprintf(stderr, "Warning: cannot configure default route\n"); | ||
572 | } | ||
573 | |||
574 | // enable interfaces | ||
575 | if (cfg.interface0.configured && cfg.interface0.ip) { | ||
576 | if (arg_debug) | ||
577 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); | ||
578 | net_if_ip(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu); | ||
579 | net_if_up(cfg.interface0.dev); | ||
580 | } | ||
581 | if (cfg.interface1.configured && cfg.interface1.ip) { | ||
582 | if (arg_debug) | ||
583 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); | ||
584 | net_if_ip(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu); | ||
585 | net_if_up(cfg.interface1.dev); | ||
586 | } | ||
587 | if (cfg.interface2.configured && cfg.interface2.ip) { | ||
588 | if (arg_debug) | ||
589 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); | ||
590 | net_if_ip(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu); | ||
591 | net_if_up(cfg.interface2.dev); | ||
592 | } | ||
593 | if (cfg.interface3.configured && cfg.interface3.ip) { | ||
594 | if (arg_debug) | ||
595 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); | ||
596 | net_if_ip(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu); | ||
597 | net_if_up(cfg.interface3.dev); | ||
598 | } | ||
599 | |||
600 | if (arg_debug) | ||
601 | printf("Network namespace enabled\n"); | ||
602 | } | ||
603 | |||
604 | // if any dns server is configured, it is time to set it now | ||
605 | fs_resolvconf(); | 764 | fs_resolvconf(); |
765 | |||
766 | //**************************** | ||
767 | // fs post-processing | ||
768 | //**************************** | ||
769 | preproc_delete_cp_command(); | ||
606 | fs_logger_print(); | 770 | fs_logger_print(); |
607 | fs_logger_change_owner(); | 771 | fs_logger_change_owner(); |
608 | 772 | ||
609 | // print network configuration | ||
610 | if (!arg_quiet) { | ||
611 | if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { | ||
612 | printf("\n"); | ||
613 | if (any_bridge_configured() || any_interface_configured()) | ||
614 | net_ifprint(); | ||
615 | if (cfg.defaultgw != 0) | ||
616 | printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); | ||
617 | if (cfg.dns1 != 0) | ||
618 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); | ||
619 | if (cfg.dns2 != 0) | ||
620 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); | ||
621 | if (cfg.dns3 != 0) | ||
622 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); | ||
623 | printf("\n"); | ||
624 | } | ||
625 | } | ||
626 | |||
627 | fs_delete_cp_command(); | ||
628 | |||
629 | //**************************** | 773 | //**************************** |
630 | // set application environment | 774 | // set application environment |
631 | //**************************** | 775 | //**************************** |
@@ -649,12 +793,6 @@ int sandbox(void* sandbox_arg) { | |||
649 | } | 793 | } |
650 | } | 794 | } |
651 | 795 | ||
652 | // set environment | ||
653 | env_defaults(); | ||
654 | |||
655 | // set user-supplied environment variables | ||
656 | env_apply(); | ||
657 | |||
658 | // set nice | 796 | // set nice |
659 | if (arg_nice) { | 797 | if (arg_nice) { |
660 | errno = 0; | 798 | errno = 0; |
@@ -668,6 +806,8 @@ int sandbox(void* sandbox_arg) { | |||
668 | 806 | ||
669 | // clean /tmp/.X11-unix sockets | 807 | // clean /tmp/.X11-unix sockets |
670 | fs_x11(); | 808 | fs_x11(); |
809 | if (arg_x11_xorg) | ||
810 | x11_xorg(); | ||
671 | 811 | ||
672 | //**************************** | 812 | //**************************** |
673 | // set security filters | 813 | // set security filters |
@@ -679,35 +819,35 @@ int sandbox(void* sandbox_arg) { | |||
679 | // set rlimits | 819 | // set rlimits |
680 | set_rlimits(); | 820 | set_rlimits(); |
681 | 821 | ||
682 | // set seccomp | 822 | // set cpu affinity |
823 | if (cfg.cpus) { | ||
824 | save_cpu(); // save cpu affinity mask to CPU_CFG file | ||
825 | set_cpu_affinity(); | ||
826 | } | ||
827 | |||
828 | // save cgroup in CGROUP_CFG file | ||
829 | if (cfg.cgroup) | ||
830 | save_cgroup(); | ||
831 | |||
832 | // set seccomp //todo: push it down after drop_privs and/or configuring noroot | ||
683 | #ifdef HAVE_SECCOMP | 833 | #ifdef HAVE_SECCOMP |
684 | // install protocol filter | 834 | // install protocol filter |
685 | if (cfg.protocol) { | 835 | if (cfg.protocol) { |
686 | protocol_filter(); // install filter | 836 | if (arg_debug) |
687 | protocol_filter_save(); // save filter in PROTOCOL_CFG | 837 | printf("Install protocol filter: %s\n", cfg.protocol); |
838 | seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter | ||
839 | protocol_filter_save(); // save filter in RUN_PROTOCOL_CFG | ||
688 | } | 840 | } |
689 | 841 | ||
690 | // if a keep list is available, disregard the drop list | 842 | // if a keep list is available, disregard the drop list |
691 | if (arg_seccomp == 1) { | 843 | if (arg_seccomp == 1) { |
692 | if (cfg.seccomp_list_keep) | 844 | if (cfg.seccomp_list_keep) |
693 | seccomp_filter_keep(); | 845 | seccomp_filter_keep(); |
694 | else if (cfg.seccomp_list_errno) | ||
695 | seccomp_filter_errno(); | ||
696 | else | 846 | else |
697 | seccomp_filter_drop(enforce_seccomp); | 847 | seccomp_filter_drop(enforce_seccomp); |
698 | } | 848 | } |
699 | #endif | 849 | #endif |
700 | 850 | ||
701 | // set cpu affinity | ||
702 | if (cfg.cpus) { | ||
703 | save_cpu(); // save cpu affinity mask to CPU_CFG file | ||
704 | set_cpu_affinity(); | ||
705 | } | ||
706 | |||
707 | // save cgroup in CGROUP_CFG file | ||
708 | if (cfg.cgroup) | ||
709 | save_cgroup(); | ||
710 | |||
711 | //**************************************** | 851 | //**************************************** |
712 | // drop privileges or create a new user namespace | 852 | // drop privileges or create a new user namespace |
713 | //**************************************** | 853 | //**************************************** |
@@ -715,7 +855,7 @@ int sandbox(void* sandbox_arg) { | |||
715 | if (arg_noroot) { | 855 | if (arg_noroot) { |
716 | int rv = unshare(CLONE_NEWUSER); | 856 | int rv = unshare(CLONE_NEWUSER); |
717 | if (rv == -1) { | 857 | if (rv == -1) { |
718 | fprintf(stderr, "Warning: cannot mount a new user namespace, going forward without it...\n"); | 858 | fprintf(stderr, "Warning: cannot create a new user namespace, going forward without it...\n"); |
719 | drop_privs(arg_nogroups); | 859 | drop_privs(arg_nogroups); |
720 | arg_noroot = 0; | 860 | arg_noroot = 0; |
721 | } | 861 | } |
@@ -739,6 +879,18 @@ int sandbox(void* sandbox_arg) { | |||
739 | printf("noroot user namespace installed\n"); | 879 | printf("noroot user namespace installed\n"); |
740 | set_caps(); | 880 | set_caps(); |
741 | } | 881 | } |
882 | |||
883 | //**************************************** | ||
884 | // Set NO_NEW_PRIVS if desired | ||
885 | //**************************************** | ||
886 | if (arg_nonewprivs) { | ||
887 | int no_new_privs = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); | ||
888 | |||
889 | if(no_new_privs != 0) | ||
890 | fprintf(stderr, "Warning: NO_NEW_PRIVS disabled, it requires a Linux kernel version 3.5 or newer.\n"); | ||
891 | else if (arg_debug) | ||
892 | printf("NO_NEW_PRIVS set\n"); | ||
893 | } | ||
742 | 894 | ||
743 | //**************************************** | 895 | //**************************************** |
744 | // fork the application and monitor it | 896 | // fork the application and monitor it |
@@ -746,13 +898,27 @@ int sandbox(void* sandbox_arg) { | |||
746 | pid_t app_pid = fork(); | 898 | pid_t app_pid = fork(); |
747 | if (app_pid == -1) | 899 | if (app_pid == -1) |
748 | errExit("fork"); | 900 | errExit("fork"); |
749 | 901 | ||
750 | if (app_pid == 0) { | 902 | if (app_pid == 0) { |
903 | #ifdef HAVE_APPARMOR | ||
904 | if (arg_apparmor) { | ||
905 | errno = 0; | ||
906 | if (aa_change_onexec("firejail-default")) { | ||
907 | fprintf(stderr, "Error: cannot confine the application using AppArmor.\n"); | ||
908 | fprintf(stderr, "Maybe firejail-default AppArmor profile is not loaded into the kernel.\n"); | ||
909 | fprintf(stderr, "As root, run \"aa-enforce firejail-default\" to load it.\n"); | ||
910 | exit(1); | ||
911 | } | ||
912 | else if (arg_debug) | ||
913 | printf("AppArmor enabled\n"); | ||
914 | } | ||
915 | #endif | ||
751 | prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died | 916 | prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died |
752 | start_application(); // start app | 917 | start_application(); // start app |
753 | } | 918 | } |
754 | 919 | ||
755 | int status = monitor_application(app_pid); // monitor application | 920 | int status = monitor_application(app_pid); // monitor application |
921 | flush_stdin(); | ||
756 | 922 | ||
757 | if (WIFEXITED(status)) { | 923 | if (WIFEXITED(status)) { |
758 | // if we had a proper exit, return that exit status | 924 | // if we had a proper exit, return that exit status |
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c new file mode 100644 index 000000000..430ffb86e --- /dev/null +++ b/src/firejail/sbox.c | |||
@@ -0,0 +1,206 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "firejail.h" | ||
21 | #include <sys/types.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <unistd.h> | ||
24 | #include <net/if.h> | ||
25 | #include <stdarg.h> | ||
26 | #include <sys/wait.h> | ||
27 | #include "../include/seccomp.h" | ||
28 | |||
29 | static struct sock_filter filter[] = { | ||
30 | VALIDATE_ARCHITECTURE, | ||
31 | EXAMINE_SYSCALL, | ||
32 | |||
33 | #if defined(__x86_64__) | ||
34 | #define X32_SYSCALL_BIT 0x40000000 | ||
35 | // handle X32 ABI | ||
36 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), | ||
37 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), | ||
38 | RETURN_ERRNO(EPERM), | ||
39 | #endif | ||
40 | |||
41 | // syscall list | ||
42 | #ifdef SYS_mount | ||
43 | BLACKLIST(SYS_mount), // mount/unmount filesystems | ||
44 | #endif | ||
45 | #ifdef SYS_umount2 | ||
46 | BLACKLIST(SYS_umount2), | ||
47 | #endif | ||
48 | #ifdef SYS_ptrace | ||
49 | BLACKLIST(SYS_ptrace), // trace processes | ||
50 | #endif | ||
51 | #ifdef SYS_kexec_file_load | ||
52 | BLACKLIST(SYS_kexec_file_load), | ||
53 | #endif | ||
54 | #ifdef SYS_kexec_load | ||
55 | BLACKLIST(SYS_kexec_load), // loading a different kernel | ||
56 | #endif | ||
57 | #ifdef SYS_name_to_handle_at | ||
58 | BLACKLIST(SYS_name_to_handle_at), | ||
59 | #endif | ||
60 | #ifdef SYS_open_by_handle_at | ||
61 | BLACKLIST(SYS_open_by_handle_at), // open by handle | ||
62 | #endif | ||
63 | #ifdef SYS_init_module | ||
64 | BLACKLIST(SYS_init_module), // kernel module handling | ||
65 | #endif | ||
66 | #ifdef SYS_finit_module // introduced in 2013 | ||
67 | BLACKLIST(SYS_finit_module), | ||
68 | #endif | ||
69 | #ifdef SYS_create_module | ||
70 | BLACKLIST(SYS_create_module), | ||
71 | #endif | ||
72 | #ifdef SYS_delete_module | ||
73 | BLACKLIST(SYS_delete_module), | ||
74 | #endif | ||
75 | #ifdef SYS_iopl | ||
76 | BLACKLIST(SYS_iopl), // io permissions | ||
77 | #endif | ||
78 | #ifdef SYS_ioperm | ||
79 | BLACKLIST(SYS_ioperm), | ||
80 | #endif | ||
81 | #ifdef SYS_iopl | ||
82 | BLACKLIST(SYS_iopl), // io permissions | ||
83 | #endif | ||
84 | #ifdef SYS_ioprio_set | ||
85 | BLACKLIST(SYS_ioprio_set), | ||
86 | #endif | ||
87 | #ifdef SYS_ni_syscall // new io permissions call on arm devices | ||
88 | BLACKLIST(SYS_ni_syscall), | ||
89 | #endif | ||
90 | #ifdef SYS_swapon | ||
91 | BLACKLIST(SYS_swapon), // swap on/off | ||
92 | #endif | ||
93 | #ifdef SYS_swapoff | ||
94 | BLACKLIST(SYS_swapoff), | ||
95 | #endif | ||
96 | #ifdef SYS_syslog | ||
97 | BLACKLIST(SYS_syslog), // kernel printk control | ||
98 | #endif | ||
99 | RETURN_ALLOW | ||
100 | }; | ||
101 | |||
102 | static struct sock_fprog prog = { | ||
103 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | ||
104 | .filter = filter, | ||
105 | }; | ||
106 | |||
107 | typedef struct sbox_config { | ||
108 | char *name; | ||
109 | char *path; | ||
110 | unsigned filters; | ||
111 | } SboxConfig; | ||
112 | |||
113 | |||
114 | int sbox_run(unsigned filter, int num, ...) { | ||
115 | EUID_ROOT(); | ||
116 | |||
117 | int i; | ||
118 | va_list valist; | ||
119 | va_start(valist, num); | ||
120 | |||
121 | // build argument list | ||
122 | char *arg[num + 1]; | ||
123 | for (i = 0; i < num; i++) | ||
124 | arg[i] = va_arg(valist, char*); | ||
125 | arg[i] = NULL; | ||
126 | va_end(valist); | ||
127 | |||
128 | if (arg_debug) { | ||
129 | printf("sbox run: "); | ||
130 | for (i = 0; i <= num; i++) | ||
131 | printf("%s ", arg[i]); | ||
132 | printf("\n"); | ||
133 | } | ||
134 | |||
135 | pid_t child = fork(); | ||
136 | if (child < 0) | ||
137 | errExit("fork"); | ||
138 | if (child == 0) { | ||
139 | // clean the new process | ||
140 | clearenv(); | ||
141 | int max = 20; // getdtablesize() is overkill for a firejail process | ||
142 | for (i = 3; i < max; i++) | ||
143 | close(i); // close open files | ||
144 | if ((filter & SBOX_ALLOW_STDIN) == 0) { | ||
145 | int fd = open("/dev/null",O_RDWR, 0); | ||
146 | if (fd != -1) { | ||
147 | dup2 (fd, STDIN_FILENO); | ||
148 | if (fd > 2) | ||
149 | close (fd); | ||
150 | } | ||
151 | else // the user could run the sandbox without /dev/null | ||
152 | close(STDIN_FILENO); | ||
153 | } | ||
154 | umask(027); | ||
155 | |||
156 | // apply filters | ||
157 | if (filter & SBOX_CAPS_NONE) { | ||
158 | caps_drop_all(); | ||
159 | } | ||
160 | else if (filter & SBOX_CAPS_NETWORK) { | ||
161 | #ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files | ||
162 | uint64_t set = ((uint64_t) 1) << CAP_NET_ADMIN; | ||
163 | set |= ((uint64_t) 1) << CAP_NET_RAW; | ||
164 | caps_set(set); | ||
165 | #endif | ||
166 | } | ||
167 | |||
168 | if (filter & SBOX_SECCOMP) { | ||
169 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
170 | perror("prctl(NO_NEW_PRIVS)"); | ||
171 | } | ||
172 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | ||
173 | perror("prctl(PR_SET_SECCOMP)"); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | if (filter & SBOX_ROOT) { | ||
178 | // elevate privileges in order to get grsecurity working | ||
179 | if (setreuid(0, 0)) | ||
180 | errExit("setreuid"); | ||
181 | if (setregid(0, 0)) | ||
182 | errExit("setregid"); | ||
183 | } | ||
184 | else if (filter & SBOX_USER) | ||
185 | drop_privs(1); | ||
186 | |||
187 | clearenv(); | ||
188 | if (arg[0]) // get rid of scan-build warning | ||
189 | execvp(arg[0], arg); | ||
190 | else | ||
191 | assert(0); | ||
192 | perror("execl"); | ||
193 | _exit(1); | ||
194 | } | ||
195 | |||
196 | int status; | ||
197 | if (waitpid(child, &status, 0) == -1 ) { | ||
198 | errExit("waitpid"); | ||
199 | } | ||
200 | if (WIFEXITED(status) && status != 0) { | ||
201 | fprintf(stderr, "Error: failed to run %s\n", arg[0]); | ||
202 | exit(1); | ||
203 | } | ||
204 | |||
205 | return status; | ||
206 | } | ||
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 7108b5a05..4a2221e98 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c | |||
@@ -20,798 +20,216 @@ | |||
20 | 20 | ||
21 | #ifdef HAVE_SECCOMP | 21 | #ifdef HAVE_SECCOMP |
22 | #include "firejail.h" | 22 | #include "firejail.h" |
23 | #include "seccomp.h" | 23 | #include "../include/seccomp.h" |
24 | |||
25 | #define SECSIZE 128 // initial filter size | ||
26 | static struct sock_filter *sfilter = NULL; | ||
27 | static int sfilter_alloc_size = 0; | ||
28 | static int sfilter_index = 0; | ||
29 | |||
30 | // debug filter | ||
31 | void filter_debug(void) { | ||
32 | // start filter | ||
33 | struct sock_filter filter[] = { | ||
34 | VALIDATE_ARCHITECTURE, | ||
35 | EXAMINE_SYSCALL | ||
36 | }; | ||
37 | 24 | ||
38 | // print sizes | 25 | char *seccomp_check_list(const char *str) { |
39 | printf("SECCOMP Filter:\n"); | 26 | assert(str); |
40 | if (sfilter == NULL) { | 27 | if (strlen(str) == 0) { |
41 | printf("SECCOMP filter not allocated\n"); | 28 | fprintf(stderr, "Error: empty syscall lists are not allowed\n"); |
42 | return; | 29 | exit(1); |
43 | } | 30 | } |
44 | if (sfilter_index < 4) | ||
45 | return; | ||
46 | 31 | ||
47 | // test the start of the filter | 32 | int len = strlen(str) + 1; |
48 | if (memcmp(sfilter, filter, sizeof(filter)) == 0) { | 33 | char *rv = malloc(len); |
49 | printf(" VALIDATE_ARCHITECTURE\n"); | 34 | if (!rv) |
50 | printf(" EXAMINE_SYSCAL\n"); | 35 | errExit("malloc"); |
51 | } | 36 | memset(rv, 0, len); |
52 | 37 | ||
53 | // loop trough blacklists | 38 | const char *ptr1 = str; |
54 | int i = 4; | 39 | char *ptr2 = rv; |
55 | while (i < sfilter_index) { | 40 | while (*ptr1 != '\0') { |
56 | // minimal parsing! | 41 | if (isalnum(*ptr1) || *ptr1 == '_' || *ptr1 == ',' || *ptr1 == ':') |
57 | unsigned char *ptr = (unsigned char *) &sfilter[i]; | 42 | *ptr2++ = *ptr1++; |
58 | int *nr = (int *) (ptr + 4); | ||
59 | if (*ptr == 0x15 && *(ptr +14) == 0xff && *(ptr + 15) == 0x7f ) { | ||
60 | printf(" WHITELIST %d %s\n", *nr, syscall_find_nr(*nr)); | ||
61 | i += 2; | ||
62 | } | ||
63 | else if (*ptr == 0x15 && *(ptr +14) == 0 && *(ptr + 15) == 0) { | ||
64 | printf(" BLACKLIST %d %s\n", *nr, syscall_find_nr(*nr)); | ||
65 | i += 2; | ||
66 | } | ||
67 | else if (*ptr == 0x15 && *(ptr +14) == 0x5 && *(ptr + 15) == 0) { | ||
68 | int err = *(ptr + 13) << 8 | *(ptr + 12); | ||
69 | printf(" ERRNO %d %s %d %s\n", *nr, syscall_find_nr(*nr), err, errno_find_nr(err)); | ||
70 | i += 2; | ||
71 | } | ||
72 | else if (*ptr == 0x06 && *(ptr +6) == 0 && *(ptr + 7) == 0 ) { | ||
73 | printf(" KILL_PROCESS\n"); | ||
74 | i++; | ||
75 | } | ||
76 | else if (*ptr == 0x06 && *(ptr +6) == 0xff && *(ptr + 7) == 0x7f ) { | ||
77 | printf(" RETURN_ALLOW\n"); | ||
78 | i++; | ||
79 | } | ||
80 | else { | 43 | else { |
81 | printf(" UNKNOWN ENTRY!!!\n"); | 44 | fprintf(stderr, "Error: invalid syscall list\n"); |
82 | i++; | 45 | exit(1); |
83 | } | 46 | } |
84 | } | 47 | } |
48 | |||
49 | return rv; | ||
85 | } | 50 | } |
86 | 51 | ||
87 | // initialize filter | ||
88 | static void filter_init(void) { | ||
89 | if (sfilter) { | ||
90 | assert(0); | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | // if (arg_debug) | ||
95 | // printf("Initialize seccomp filter\n"); | ||
96 | // allocate a filter of SECSIZE | ||
97 | sfilter = malloc(sizeof(struct sock_filter) * SECSIZE); | ||
98 | if (!sfilter) | ||
99 | errExit("malloc"); | ||
100 | memset(sfilter, 0, sizeof(struct sock_filter) * SECSIZE); | ||
101 | sfilter_alloc_size = SECSIZE; | ||
102 | |||
103 | // copy the start entries | ||
104 | struct sock_filter filter[] = { | ||
105 | VALIDATE_ARCHITECTURE, | ||
106 | EXAMINE_SYSCALL | ||
107 | }; | ||
108 | sfilter_index = sizeof(filter) / sizeof(struct sock_filter); | ||
109 | memcpy(sfilter, filter, sizeof(filter)); | ||
110 | } | ||
111 | |||
112 | static void filter_realloc(void) { | ||
113 | assert(sfilter); | ||
114 | assert(sfilter_alloc_size); | ||
115 | assert(sfilter_index); | ||
116 | if (arg_debug) | ||
117 | printf("Allocating more seccomp filter entries\n"); | ||
118 | |||
119 | // allocate the new memory | ||
120 | struct sock_filter *old = sfilter; | ||
121 | sfilter = malloc(sizeof(struct sock_filter) * (sfilter_alloc_size + SECSIZE)); | ||
122 | if (!sfilter) | ||
123 | errExit("malloc"); | ||
124 | memset(sfilter, 0, sizeof(struct sock_filter) * (sfilter_alloc_size + SECSIZE)); | ||
125 | |||
126 | // copy old filter | ||
127 | memcpy(sfilter, old, sizeof(struct sock_filter) * sfilter_alloc_size); | ||
128 | sfilter_alloc_size += SECSIZE; | ||
129 | } | ||
130 | |||
131 | static void filter_add_whitelist(int syscall, int arg) { | ||
132 | (void) arg; | ||
133 | assert(sfilter); | ||
134 | assert(sfilter_alloc_size); | ||
135 | assert(sfilter_index); | ||
136 | // if (arg_debug) | ||
137 | // printf("Whitelisting syscall %d %s\n", syscall, syscall_find_nr(syscall)); | ||
138 | |||
139 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
140 | filter_realloc(); | ||
141 | |||
142 | struct sock_filter filter[] = { | ||
143 | WHITELIST(syscall) | ||
144 | }; | ||
145 | #if 0 | ||
146 | { | ||
147 | int i; | ||
148 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
149 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
150 | printf("%x, ", (*ptr) & 0xff); | ||
151 | printf("\n"); | ||
152 | } | ||
153 | #endif | ||
154 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
155 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
156 | } | ||
157 | |||
158 | static void filter_add_blacklist(int syscall, int arg) { | ||
159 | (void) arg; | ||
160 | assert(sfilter); | ||
161 | assert(sfilter_alloc_size); | ||
162 | assert(sfilter_index); | ||
163 | // if (arg_debug) | ||
164 | // printf("Blacklisting syscall %d %s\n", syscall, syscall_find_nr(syscall)); | ||
165 | |||
166 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
167 | filter_realloc(); | ||
168 | |||
169 | struct sock_filter filter[] = { | ||
170 | BLACKLIST(syscall) | ||
171 | }; | ||
172 | #if 0 | ||
173 | { | ||
174 | int i; | ||
175 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
176 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
177 | printf("%x, ", (*ptr) & 0xff); | ||
178 | printf("\n"); | ||
179 | } | ||
180 | #endif | ||
181 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
182 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
183 | } | ||
184 | |||
185 | static void filter_add_errno(int syscall, int arg) { | ||
186 | assert(sfilter); | ||
187 | assert(sfilter_alloc_size); | ||
188 | assert(sfilter_index); | ||
189 | // if (arg_debug) | ||
190 | // printf("Errno syscall %d %d %s\n", syscall, arg, syscall_find_nr(syscall)); | ||
191 | |||
192 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
193 | filter_realloc(); | ||
194 | |||
195 | struct sock_filter filter[] = { | ||
196 | BLACKLIST_ERRNO(syscall, arg) | ||
197 | }; | ||
198 | #if 0 | ||
199 | { | ||
200 | int i; | ||
201 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
202 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
203 | printf("%x, ", (*ptr) & 0xff); | ||
204 | printf("\n"); | ||
205 | } | ||
206 | #endif | ||
207 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
208 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
209 | } | ||
210 | |||
211 | static void filter_end_blacklist(void) { | ||
212 | assert(sfilter); | ||
213 | assert(sfilter_alloc_size); | ||
214 | assert(sfilter_index); | ||
215 | // if (arg_debug) | ||
216 | // printf("Ending syscall filter\n"); | ||
217 | |||
218 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
219 | filter_realloc(); | ||
220 | |||
221 | struct sock_filter filter[] = { | ||
222 | RETURN_ALLOW | ||
223 | }; | ||
224 | #if 0 | ||
225 | { | ||
226 | int i; | ||
227 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
228 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
229 | printf("%x, ", (*ptr) & 0xff); | ||
230 | printf("\n"); | ||
231 | } | ||
232 | #endif | ||
233 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
234 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
235 | } | ||
236 | |||
237 | static void filter_end_whitelist(void) { | ||
238 | assert(sfilter); | ||
239 | assert(sfilter_alloc_size); | ||
240 | assert(sfilter_index); | ||
241 | if (arg_debug) | ||
242 | printf("Ending syscall filter\n"); | ||
243 | |||
244 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
245 | filter_realloc(); | ||
246 | |||
247 | struct sock_filter filter[] = { | ||
248 | KILL_PROCESS | ||
249 | }; | ||
250 | #if 0 | ||
251 | { | ||
252 | int i; | ||
253 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
254 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
255 | printf("%x, ", (*ptr) & 0xff); | ||
256 | printf("\n"); | ||
257 | } | ||
258 | #endif | ||
259 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
260 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
261 | } | ||
262 | |||
263 | |||
264 | // save seccomp filter in /run/firejail/mnt/seccomp | ||
265 | static void write_seccomp_file(void) { | ||
266 | fs_build_mnt_dir(); | ||
267 | assert(sfilter); | ||
268 | 52 | ||
269 | int fd = open(RUN_SECCOMP_CFG, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); | 53 | int seccomp_load(const char *fname) { |
270 | if (fd == -1) | 54 | assert(fname); |
271 | errExit("open"); | ||
272 | |||
273 | if (arg_debug) | ||
274 | printf("Save seccomp filter, size %u bytes\n", (unsigned) (sfilter_index * sizeof(struct sock_filter))); | ||
275 | errno = 0; | ||
276 | ssize_t sz = write(fd, sfilter, sfilter_index * sizeof(struct sock_filter)); | ||
277 | if (sz != (ssize_t)(sfilter_index * sizeof(struct sock_filter))) { | ||
278 | fprintf(stderr, "Error: cannot save seccomp filter\n"); | ||
279 | exit(1); | ||
280 | } | ||
281 | close(fd); | ||
282 | if (chown(RUN_SECCOMP_CFG, 0, 0) < 0) | ||
283 | errExit("chown"); | ||
284 | } | ||
285 | |||
286 | // read seccomp filter from /run/firejail/mnt/seccomp | ||
287 | static void read_seccomp_file(const char *fname) { | ||
288 | assert(sfilter == NULL && sfilter_index == 0); | ||
289 | 55 | ||
290 | // check file | 56 | // check file |
291 | struct stat s; | 57 | struct stat s; |
292 | if (stat(fname, &s) == -1) { | 58 | if (stat(fname, &s) == -1) { |
293 | fprintf(stderr, "Warning: seccomp file not found\n"); | 59 | fprintf(stderr, "Error: cannot read protocol filter file\n"); |
294 | return; | ||
295 | } | ||
296 | ssize_t sz = s.st_size; | ||
297 | if (sz == 0 || (sz % sizeof(struct sock_filter)) != 0) { | ||
298 | fprintf(stderr, "Error: invalid seccomp file\n"); | ||
299 | exit(1); | 60 | exit(1); |
300 | } | 61 | } |
301 | sfilter = malloc(sz); | 62 | int size = s.st_size; |
302 | if (!sfilter) | 63 | unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); |
303 | errExit("malloc"); | 64 | //printf("size %d, entries %d\n", s.st_size, entries); |
304 | 65 | ||
305 | // read file | 66 | // read filter |
306 | /* coverity[toctou] */ | 67 | struct sock_filter filter[entries]; |
307 | int fd = open(fname,O_RDONLY); | 68 | memset(&filter[0], 0, sizeof(filter)); |
308 | if (fd == -1) | 69 | int src = open(fname, O_RDONLY); |
309 | errExit("open"); | 70 | int rd = 0; |
310 | errno = 0; | 71 | while (rd < size) { |
311 | ssize_t size = read(fd, sfilter, sz); | 72 | int rv = read(src, (unsigned char *) filter + rd, size - rd); |
312 | if (size != sz) { | 73 | if (rv == -1) { |
313 | fprintf(stderr, "Error: invalid seccomp file\n"); | 74 | fprintf(stderr, "Error: cannot read %s file\n", fname); |
314 | exit(1); | 75 | exit(1); |
76 | } | ||
77 | rd += rv; | ||
315 | } | 78 | } |
316 | sfilter_index = sz / sizeof(struct sock_filter); | 79 | close(src); |
317 | 80 | ||
318 | if (arg_debug) | 81 | // install filter |
319 | printf("Read seccomp filter, size %u bytes\n", (unsigned) (sfilter_index * sizeof(struct sock_filter))); | 82 | struct sock_fprog prog = { |
83 | .len = entries, | ||
84 | .filter = filter, | ||
85 | }; | ||
320 | 86 | ||
321 | close(fd); | 87 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { |
88 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | ||
89 | return 1; | ||
90 | } | ||
322 | 91 | ||
323 | if (arg_debug) | 92 | return 0; |
324 | filter_debug(); | ||
325 | } | 93 | } |
326 | 94 | ||
95 | |||
96 | |||
97 | |||
327 | // i386 filter installed on amd64 architectures | 98 | // i386 filter installed on amd64 architectures |
328 | void seccomp_filter_32(void) { | 99 | void seccomp_filter_32(void) { |
329 | // hardcoded syscall values | 100 | if (arg_debug) |
330 | struct sock_filter filter[] = { | 101 | printf("Build secondary 32-bit filter\n"); |
331 | VALIDATE_ARCHITECTURE_32, | ||
332 | EXAMINE_SYSCALL, | ||
333 | BLACKLIST(21), // mount | ||
334 | BLACKLIST(52), // umount2 | ||
335 | BLACKLIST(26), // ptrace | ||
336 | BLACKLIST(283), // kexec_load | ||
337 | BLACKLIST(342), // open_by_handle_at | ||
338 | BLACKLIST(128), // init_module | ||
339 | BLACKLIST(350), // finit_module | ||
340 | BLACKLIST(129), // delete_module | ||
341 | BLACKLIST(110), // iopl | ||
342 | BLACKLIST(101), // ioperm | ||
343 | BLACKLIST(87), // swapon | ||
344 | BLACKLIST(115), // swapoff | ||
345 | BLACKLIST(103), // syslog | ||
346 | BLACKLIST(347), // process_vm_readv | ||
347 | BLACKLIST(348), // process_vm_writev | ||
348 | BLACKLIST(135), // sysfs | ||
349 | BLACKLIST(149), // _sysctl | ||
350 | BLACKLIST(124), // adjtimex | ||
351 | BLACKLIST(343), // clock_adjtime | ||
352 | BLACKLIST(253), // lookup_dcookie | ||
353 | BLACKLIST(336), // perf_event_open | ||
354 | BLACKLIST(338), // fanotify_init | ||
355 | BLACKLIST(349), // kcmp | ||
356 | BLACKLIST(286), // add_key | ||
357 | BLACKLIST(287), // request_key | ||
358 | BLACKLIST(288), // keyctl | ||
359 | BLACKLIST(86), // uselib | ||
360 | BLACKLIST(51), // acct | ||
361 | BLACKLIST(123), // modify_ldt | ||
362 | BLACKLIST(217), // pivot_root | ||
363 | BLACKLIST(245), // io_setup | ||
364 | BLACKLIST(246), // io_destroy | ||
365 | BLACKLIST(247), // io_getevents | ||
366 | BLACKLIST(248), // io_submit | ||
367 | BLACKLIST(249), // io_cancel | ||
368 | BLACKLIST(257), // remap_file_pages | ||
369 | BLACKLIST(274), // mbind | ||
370 | BLACKLIST(275), // get_mempolicy | ||
371 | BLACKLIST(276), // set_mempolicy | ||
372 | BLACKLIST(294), // migrate_pages | ||
373 | BLACKLIST(317), // move_pages | ||
374 | BLACKLIST(316), // vmsplice | ||
375 | BLACKLIST(61), // chroot | ||
376 | BLACKLIST(88), // reboot | ||
377 | BLACKLIST(169), // nfsservctl | ||
378 | BLACKLIST(130), // get_kernel_syms | ||
379 | RETURN_ALLOW | ||
380 | }; | ||
381 | 102 | ||
382 | struct sock_fprog prog = { | 103 | // build the seccomp filter as a regular user |
383 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | 104 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, |
384 | .filter = filter, | 105 | PATH_FSECCOMP, "secondary", "32", RUN_SECCOMP_I386); |
385 | }; | 106 | if (rv) |
107 | exit(rv); | ||
386 | 108 | ||
387 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 109 | if (seccomp_load(RUN_SECCOMP_I386) == 0) { |
388 | ; | 110 | if (arg_debug) |
389 | } | 111 | printf("Dual i386/amd64 seccomp filter configured\n"); |
390 | else if (arg_debug) { | ||
391 | printf("Dual i386/amd64 seccomp filter configured\n"); | ||
392 | } | 112 | } |
393 | } | 113 | } |
394 | 114 | ||
395 | // amd64 filter installed on i386 architectures | 115 | // amd64 filter installed on i386 architectures |
396 | void seccomp_filter_64(void) { | 116 | void seccomp_filter_64(void) { |
397 | // hardcoded syscall values | 117 | if (arg_debug) |
398 | struct sock_filter filter[] = { | 118 | printf("Build secondary 64-bit filter\n"); |
399 | VALIDATE_ARCHITECTURE_64, | ||
400 | EXAMINE_SYSCALL, | ||
401 | BLACKLIST(165), // mount | ||
402 | BLACKLIST(166), // umount2 | ||
403 | BLACKLIST(101), // ptrace | ||
404 | BLACKLIST(246), // kexec_load | ||
405 | BLACKLIST(304), // open_by_handle_at | ||
406 | BLACKLIST(175), // init_module | ||
407 | BLACKLIST(313), // finit_module | ||
408 | BLACKLIST(176), // delete_module | ||
409 | BLACKLIST(172), // iopl | ||
410 | BLACKLIST(173), // ioperm | ||
411 | BLACKLIST(167), // swapon | ||
412 | BLACKLIST(168), // swapoff | ||
413 | BLACKLIST(103), // syslog | ||
414 | BLACKLIST(310), // process_vm_readv | ||
415 | BLACKLIST(311), // process_vm_writev | ||
416 | BLACKLIST(139), // sysfs | ||
417 | BLACKLIST(156), // _sysctl | ||
418 | BLACKLIST(159), // adjtimex | ||
419 | BLACKLIST(305), // clock_adjtime | ||
420 | BLACKLIST(212), // lookup_dcookie | ||
421 | BLACKLIST(298), // perf_event_open | ||
422 | BLACKLIST(300), // fanotify_init | ||
423 | BLACKLIST(312), // kcmp | ||
424 | BLACKLIST(248), // add_key | ||
425 | BLACKLIST(249), // request_key | ||
426 | BLACKLIST(250), // keyctl | ||
427 | BLACKLIST(134), // uselib | ||
428 | BLACKLIST(163), // acct | ||
429 | BLACKLIST(154), // modify_ldt | ||
430 | BLACKLIST(155), // pivot_root | ||
431 | BLACKLIST(206), // io_setup | ||
432 | BLACKLIST(207), // io_destroy | ||
433 | BLACKLIST(208), // io_getevents | ||
434 | BLACKLIST(209), // io_submit | ||
435 | BLACKLIST(210), // io_cancel | ||
436 | BLACKLIST(216), // remap_file_pages | ||
437 | BLACKLIST(237), // mbind | ||
438 | BLACKLIST(239), // get_mempolicy | ||
439 | BLACKLIST(238), // set_mempolicy | ||
440 | BLACKLIST(256), // migrate_pages | ||
441 | BLACKLIST(279), // move_pages | ||
442 | BLACKLIST(278), // vmsplice | ||
443 | BLACKLIST(161), // chroot | ||
444 | BLACKLIST(184), // tuxcall | ||
445 | BLACKLIST(169), // reboot | ||
446 | BLACKLIST(180), // nfsservctl | ||
447 | BLACKLIST(177), // get_kernel_syms | ||
448 | RETURN_ALLOW | ||
449 | }; | ||
450 | 119 | ||
451 | struct sock_fprog prog = { | 120 | // build the seccomp filter as a regular user |
452 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | 121 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, |
453 | .filter = filter, | 122 | PATH_FSECCOMP, "secondary", "64", RUN_SECCOMP_AMD64); |
454 | }; | 123 | if (rv) |
124 | exit(rv); | ||
455 | 125 | ||
456 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 126 | if (seccomp_load(RUN_SECCOMP_AMD64) == 0) { |
457 | ; | 127 | if (arg_debug) |
458 | } | 128 | printf("Dual i386/amd64 seccomp filter configured\n"); |
459 | else if (arg_debug) { | ||
460 | printf("Dual i386/amd64 seccomp filter configured\n"); | ||
461 | } | 129 | } |
462 | } | 130 | } |
463 | 131 | ||
464 | 132 | ||
465 | // drop filter for seccomp option | 133 | // drop filter for seccomp option |
466 | int seccomp_filter_drop(int enforce_seccomp) { | 134 | int seccomp_filter_drop(int enforce_seccomp) { |
467 | filter_init(); | ||
468 | |||
469 | // default seccomp | 135 | // default seccomp |
470 | if (cfg.seccomp_list_drop == NULL) { | 136 | if (cfg.seccomp_list_drop == NULL && cfg.seccomp_list == NULL) { |
471 | #if defined(__x86_64__) | 137 | #if defined(__x86_64__) |
472 | seccomp_filter_32(); | 138 | seccomp_filter_32(); |
473 | #endif | 139 | #endif |
474 | #if defined(__i386__) | 140 | #if defined(__i386__) |
475 | seccomp_filter_64(); | 141 | seccomp_filter_64(); |
476 | #endif | 142 | #endif |
143 | if (arg_debug) | ||
144 | printf("Build default seccomp filter\n"); | ||
145 | // build the seccomp filter as a regular user | ||
146 | int rv; | ||
147 | if (arg_allow_debuggers) | ||
148 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, | ||
149 | PATH_FSECCOMP, "default", RUN_SECCOMP_CFG, "allow-debuggers"); | ||
150 | else | ||
151 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, | ||
152 | PATH_FSECCOMP, "default", RUN_SECCOMP_CFG); | ||
153 | if (rv) | ||
154 | exit(rv); | ||
155 | } | ||
477 | 156 | ||
478 | #ifdef SYS_mount | 157 | // default seccomp filter with additional drop list |
479 | filter_add_blacklist(SYS_mount, 0); | 158 | else if (cfg.seccomp_list && cfg.seccomp_list_drop == NULL) { |
480 | #endif | 159 | #if defined(__x86_64__) |
481 | #ifdef SYS_umount2 | 160 | seccomp_filter_32(); |
482 | filter_add_blacklist(SYS_umount2, 0); | ||
483 | #endif | ||
484 | #ifdef SYS_ptrace | ||
485 | filter_add_blacklist(SYS_ptrace, 0); | ||
486 | #endif | ||
487 | #ifdef SYS_kexec_load | ||
488 | filter_add_blacklist(SYS_kexec_load, 0); | ||
489 | #endif | ||
490 | #ifdef SYS_kexec_file_load | ||
491 | filter_add_blacklist(SYS_kexec_file_load, 0); | ||
492 | #endif | ||
493 | #ifdef SYS_open_by_handle_at | ||
494 | filter_add_blacklist(SYS_open_by_handle_at, 0); | ||
495 | #endif | ||
496 | #ifdef SYS_init_module | ||
497 | filter_add_blacklist(SYS_init_module, 0); | ||
498 | #endif | ||
499 | #ifdef SYS_finit_module // introduced in 2013 | ||
500 | filter_add_blacklist(SYS_finit_module, 0); | ||
501 | #endif | ||
502 | #ifdef SYS_delete_module | ||
503 | filter_add_blacklist(SYS_delete_module, 0); | ||
504 | #endif | ||
505 | #ifdef SYS_iopl | ||
506 | filter_add_blacklist(SYS_iopl, 0); | ||
507 | #endif | ||
508 | #ifdef SYS_ioperm | ||
509 | filter_add_blacklist(SYS_ioperm, 0); | ||
510 | #endif | ||
511 | #ifdef SYS_ni_syscall // new io permissions call on arm devices | ||
512 | filter_add_blacklist(SYS_ni_syscall, 0); | ||
513 | #endif | ||
514 | #ifdef SYS_swapon | ||
515 | filter_add_blacklist(SYS_swapon, 0); | ||
516 | #endif | ||
517 | #ifdef SYS_swapoff | ||
518 | filter_add_blacklist(SYS_swapoff, 0); | ||
519 | #endif | ||
520 | #ifdef SYS_syslog | ||
521 | filter_add_blacklist(SYS_syslog, 0); | ||
522 | #endif | ||
523 | #ifdef SYS_process_vm_readv | ||
524 | filter_add_blacklist(SYS_process_vm_readv, 0); | ||
525 | #endif | 161 | #endif |
526 | #ifdef SYS_process_vm_writev | 162 | #if defined(__i386__) |
527 | filter_add_blacklist(SYS_process_vm_writev, 0); | 163 | seccomp_filter_64(); |
528 | #endif | 164 | #endif |
529 | 165 | if (arg_debug) | |
530 | // mknod removed in 0.9.29 - it brakes Zotero extension | 166 | printf("Build default+drop seccomp filter\n"); |
531 | //#ifdef SYS_mknod | ||
532 | // filter_add_blacklist(SYS_mknod, 0); | ||
533 | //#endif | ||
534 | 167 | ||
535 | // new syscalls in 0.9,23 | 168 | // build the seccomp filter as a regular user |
536 | #ifdef SYS_sysfs | 169 | int rv; |
537 | filter_add_blacklist(SYS_sysfs, 0); | 170 | if (arg_allow_debuggers) |
538 | #endif | 171 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 6, |
539 | #ifdef SYS__sysctl | 172 | PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, cfg.seccomp_list, "allow-debuggers"); |
540 | filter_add_blacklist(SYS__sysctl, 0); | 173 | else |
541 | #endif | 174 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, |
542 | #ifdef SYS_adjtimex | 175 | PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, cfg.seccomp_list); |
543 | filter_add_blacklist(SYS_adjtimex, 0); | 176 | if (rv) |
544 | #endif | 177 | exit(rv); |
545 | #ifdef SYS_clock_adjtime | ||
546 | filter_add_blacklist(SYS_clock_adjtime, 0); | ||
547 | #endif | ||
548 | #ifdef SYS_lookup_dcookie | ||
549 | filter_add_blacklist(SYS_lookup_dcookie, 0); | ||
550 | #endif | ||
551 | #ifdef SYS_perf_event_open | ||
552 | filter_add_blacklist(SYS_perf_event_open, 0); | ||
553 | #endif | ||
554 | #ifdef SYS_fanotify_init | ||
555 | filter_add_blacklist(SYS_fanotify_init, 0); | ||
556 | #endif | ||
557 | #ifdef SYS_kcmp | ||
558 | filter_add_blacklist(SYS_kcmp, 0); | ||
559 | #endif | ||
560 | |||
561 | // 0.9.32 | ||
562 | #ifdef SYS_add_key | ||
563 | filter_add_blacklist(SYS_add_key, 0); | ||
564 | #endif | ||
565 | #ifdef SYS_request_key | ||
566 | filter_add_blacklist(SYS_request_key, 0); | ||
567 | #endif | ||
568 | #ifdef SYS_keyctl | ||
569 | filter_add_blacklist(SYS_keyctl, 0); | ||
570 | #endif | ||
571 | #ifdef SYS_uselib | ||
572 | filter_add_blacklist(SYS_uselib, 0); | ||
573 | #endif | ||
574 | #ifdef SYS_acct | ||
575 | filter_add_blacklist(SYS_acct, 0); | ||
576 | #endif | ||
577 | #ifdef SYS_modify_ldt | ||
578 | filter_add_blacklist(SYS_modify_ldt, 0); | ||
579 | #endif | ||
580 | //#ifdef SYS_unshare | ||
581 | // filter_add_blacklist(SYS_unshare, 0); | ||
582 | //#endif | ||
583 | #ifdef SYS_pivot_root | ||
584 | filter_add_blacklist(SYS_pivot_root, 0); | ||
585 | #endif | ||
586 | //#ifdef SYS_quotactl | ||
587 | // filter_add_blacklist(SYS_quotactl, 0); | ||
588 | //#endif | ||
589 | #ifdef SYS_io_setup | ||
590 | filter_add_blacklist(SYS_io_setup, 0); | ||
591 | #endif | ||
592 | #ifdef SYS_io_destroy | ||
593 | filter_add_blacklist(SYS_io_destroy, 0); | ||
594 | #endif | ||
595 | #ifdef SYS_io_getevents | ||
596 | filter_add_blacklist(SYS_io_getevents, 0); | ||
597 | #endif | ||
598 | #ifdef SYS_io_submit | ||
599 | filter_add_blacklist(SYS_io_submit, 0); | ||
600 | #endif | ||
601 | #ifdef SYS_io_cancel | ||
602 | filter_add_blacklist(SYS_io_cancel, 0); | ||
603 | #endif | ||
604 | #ifdef SYS_remap_file_pages | ||
605 | filter_add_blacklist(SYS_remap_file_pages, 0); | ||
606 | #endif | ||
607 | #ifdef SYS_mbind | ||
608 | filter_add_blacklist(SYS_mbind, 0); | ||
609 | #endif | ||
610 | #ifdef SYS_get_mempolicy | ||
611 | filter_add_blacklist(SYS_get_mempolicy, 0); | ||
612 | #endif | ||
613 | #ifdef SYS_set_mempolicy | ||
614 | filter_add_blacklist(SYS_set_mempolicy, 0); | ||
615 | #endif | ||
616 | #ifdef SYS_migrate_pages | ||
617 | filter_add_blacklist(SYS_migrate_pages, 0); | ||
618 | #endif | ||
619 | #ifdef SYS_move_pages | ||
620 | filter_add_blacklist(SYS_move_pages, 0); | ||
621 | #endif | ||
622 | #ifdef SYS_vmsplice | ||
623 | filter_add_blacklist(SYS_vmsplice, 0); | ||
624 | #endif | ||
625 | #ifdef SYS_chroot | ||
626 | filter_add_blacklist(SYS_chroot, 0); | ||
627 | #endif | ||
628 | //#ifdef SYS_set_robust_list | ||
629 | // filter_add_blacklist(SYS_set_robust_list, 0); | ||
630 | //#endif | ||
631 | //#ifdef SYS_get_robust_list | ||
632 | // filter_add_blacklist(SYS_get_robust_list, 0); | ||
633 | //#endif | ||
634 | |||
635 | // CHECK_SECCOMP(seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(clone), 1, | ||
636 | // SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER))); | ||
637 | |||
638 | // 0.9.39 | ||
639 | #ifdef SYS_tuxcall | ||
640 | filter_add_blacklist(SYS_tuxcall, 0); | ||
641 | #endif | ||
642 | #ifdef SYS_reboot | ||
643 | filter_add_blacklist(SYS_reboot, 0); | ||
644 | #endif | ||
645 | #ifdef SYS_nfsservctl | ||
646 | filter_add_blacklist(SYS_nfsservctl, 0); | ||
647 | #endif | ||
648 | #ifdef SYS_get_kernel_syms | ||
649 | filter_add_blacklist(SYS_get_kernel_syms, 0); | ||
650 | #endif | ||
651 | } | ||
652 | |||
653 | // default seccomp filter with additional drop list | ||
654 | if (cfg.seccomp_list && cfg.seccomp_list_drop == NULL) { | ||
655 | if (syscall_check_list(cfg.seccomp_list, filter_add_blacklist, 0)) { | ||
656 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | ||
657 | exit(1); | ||
658 | } | ||
659 | } | ||
660 | // drop list | ||
661 | else if (cfg.seccomp_list == NULL && cfg.seccomp_list_drop) { | ||
662 | if (syscall_check_list(cfg.seccomp_list_drop, filter_add_blacklist, 0)) { | ||
663 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | ||
664 | exit(1); | ||
665 | } | ||
666 | } | 178 | } |
667 | 179 | ||
668 | 180 | // drop list without defaults - secondary filters are not installed | |
669 | filter_end_blacklist(); | 181 | else if (cfg.seccomp_list == NULL && cfg.seccomp_list_drop) { |
670 | if (arg_debug) | 182 | if (arg_debug) |
671 | filter_debug(); | 183 | printf("Build drop seccomp filter\n"); |
672 | 184 | ||
673 | // save seccomp filter in /tmp/firejail/mnt/seccomp | 185 | // build the seccomp filter as a regular user |
674 | // in order to use it in --join operations | 186 | int rv; |
675 | write_seccomp_file(); | 187 | if (arg_allow_debuggers) |
676 | 188 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, | |
677 | 189 | PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, cfg.seccomp_list_drop, "allow-debuggers"); | |
678 | struct sock_fprog prog = { | ||
679 | .len = sfilter_index, | ||
680 | .filter = sfilter, | ||
681 | }; | ||
682 | |||
683 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
684 | if (enforce_seccomp) { | ||
685 | fprintf(stderr, "Error: a seccomp-enabled Linux kernel is required, exiting...\n"); | ||
686 | exit(1); | ||
687 | } | ||
688 | else | 190 | else |
689 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | 191 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, |
192 | PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, cfg.seccomp_list_drop); | ||
690 | 193 | ||
691 | return 1; | 194 | if (rv) |
195 | exit(rv); | ||
692 | } | 196 | } |
693 | 197 | else { | |
694 | return 0; | 198 | assert(0); |
695 | } | ||
696 | |||
697 | // keep filter for seccomp option | ||
698 | int seccomp_filter_keep(void) { | ||
699 | filter_init(); | ||
700 | |||
701 | // these 4 syscalls are used by firejail after the seccomp filter is initialized | ||
702 | filter_add_whitelist(SYS_setuid, 0); | ||
703 | filter_add_whitelist(SYS_setgid, 0); | ||
704 | filter_add_whitelist(SYS_setgroups, 0); | ||
705 | filter_add_whitelist(SYS_dup, 0); | ||
706 | |||
707 | // apply keep list | ||
708 | if (cfg.seccomp_list_keep) { | ||
709 | if (syscall_check_list(cfg.seccomp_list_keep, filter_add_whitelist, 0)) { | ||
710 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | ||
711 | exit(1); | ||
712 | } | ||
713 | } | 199 | } |
714 | 200 | ||
715 | filter_end_whitelist(); | 201 | // load the filter |
716 | if (arg_debug) | 202 | if (seccomp_load(RUN_SECCOMP_CFG) == 0) { |
717 | filter_debug(); | 203 | if (arg_debug) |
718 | 204 | printf("seccomp filter configured\n"); | |
719 | // save seccomp filter in /tmp/firejail/mnt/seccomp | ||
720 | // in order to use it in --join operations | ||
721 | write_seccomp_file(); | ||
722 | |||
723 | |||
724 | struct sock_fprog prog = { | ||
725 | .len = sfilter_index, | ||
726 | .filter = sfilter, | ||
727 | }; | ||
728 | |||
729 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
730 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | ||
731 | return 1; | ||
732 | } | 205 | } |
733 | else if (arg_debug) { | 206 | else if (enforce_seccomp) { |
734 | printf("seccomp enabled\n"); | 207 | fprintf(stderr, "Error: a seccomp-enabled Linux kernel is required, exiting...\n"); |
208 | exit(1); | ||
735 | } | 209 | } |
736 | 210 | ||
737 | return 0; | ||
738 | } | ||
739 | |||
740 | // errno filter for seccomp option | ||
741 | int seccomp_filter_errno(void) { | ||
742 | int i; | ||
743 | int higest_errno = errno_highest_nr(); | ||
744 | filter_init(); | ||
745 | |||
746 | // apply errno list | ||
747 | |||
748 | for (i = 0; i < higest_errno; i++) { | ||
749 | if (cfg.seccomp_list_errno[i]) { | ||
750 | if (syscall_check_list(cfg.seccomp_list_errno[i], filter_add_errno, i)) { | ||
751 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | ||
752 | exit(1); | ||
753 | } | ||
754 | } | ||
755 | } | ||
756 | |||
757 | filter_end_blacklist(); | ||
758 | if (arg_debug) | 211 | if (arg_debug) |
759 | filter_debug(); | 212 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, |
760 | 213 | PATH_FSECCOMP, "print", RUN_SECCOMP_CFG); | |
761 | // save seccomp filter in /tmp/firejail/mnt/seccomp | ||
762 | // in order to use it in --join operations | ||
763 | write_seccomp_file(); | ||
764 | |||
765 | struct sock_fprog prog = { | ||
766 | .len = sfilter_index, | ||
767 | .filter = sfilter, | ||
768 | }; | ||
769 | |||
770 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
771 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | ||
772 | return 1; | ||
773 | } | ||
774 | else if (arg_debug) { | ||
775 | printf("seccomp enabled\n"); | ||
776 | } | ||
777 | 214 | ||
778 | return 0; | 215 | return seccomp_load(RUN_SECCOMP_CFG); |
779 | } | 216 | } |
780 | 217 | ||
781 | 218 | // keep filter for seccomp option | |
782 | 219 | int seccomp_filter_keep(void) { | |
783 | void seccomp_set(void) { | 220 | if (arg_debug) |
784 | // read seccomp filter from /tmp/firejail/mnt/seccomp | 221 | printf("Build drop seccomp filter\n"); |
785 | read_seccomp_file(RUN_SECCOMP_CFG); | ||
786 | |||
787 | // apply filter | ||
788 | struct sock_fprog prog = { | ||
789 | .len = sfilter_index, | ||
790 | .filter = sfilter, | ||
791 | }; | ||
792 | 222 | ||
793 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 223 | // build the seccomp filter as a regular user |
794 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | 224 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, |
795 | return; | 225 | PATH_FSECCOMP, "keep", RUN_SECCOMP_CFG, cfg.seccomp_list_keep); |
796 | } | 226 | if (rv) |
797 | else if (arg_debug) { | 227 | exit(rv); |
798 | printf("seccomp enabled\n"); | 228 | if (arg_debug) |
799 | } | 229 | printf("seccomp filter configured\n"); |
800 | } | ||
801 | |||
802 | void seccomp_print_filter_name(const char *name) { | ||
803 | EUID_ASSERT(); | ||
804 | if (!name || strlen(name) == 0) { | ||
805 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
806 | exit(1); | ||
807 | } | ||
808 | pid_t pid; | ||
809 | if (name2pid(name, &pid)) { | ||
810 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
811 | exit(1); | ||
812 | } | ||
813 | 230 | ||
814 | seccomp_print_filter(pid); | 231 | |
232 | return seccomp_load(RUN_SECCOMP_CFG); | ||
815 | } | 233 | } |
816 | 234 | ||
817 | void seccomp_print_filter(pid_t pid) { | 235 | void seccomp_print_filter(pid_t pid) { |
@@ -853,10 +271,11 @@ void seccomp_print_filter(pid_t pid) { | |||
853 | exit(1); | 271 | exit(1); |
854 | } | 272 | } |
855 | 273 | ||
856 | // read and print the filter | 274 | // read and print the filter - run this as root, the user doesn't have access |
857 | read_seccomp_file(fname); | 275 | int rv = sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, |
858 | drop_privs(1); | 276 | PATH_FSECCOMP, "print", fname); |
859 | filter_debug(); | 277 | if (rv) |
278 | exit(rv); | ||
860 | free(fname); | 279 | free(fname); |
861 | 280 | ||
862 | exit(0); | 281 | exit(0); |
diff --git a/src/firejail/shutdown.c b/src/firejail/shutdown.c index 8d8035bfb..c23e87321 100644 --- a/src/firejail/shutdown.c +++ b/src/firejail/shutdown.c | |||
@@ -23,22 +23,6 @@ | |||
23 | #include <fcntl.h> | 23 | #include <fcntl.h> |
24 | #include <sys/prctl.h> | 24 | #include <sys/prctl.h> |
25 | 25 | ||
26 | void shut_name(const char *name) { | ||
27 | EUID_ASSERT(); | ||
28 | if (!name || strlen(name) == 0) { | ||
29 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
30 | exit(1); | ||
31 | } | ||
32 | |||
33 | pid_t pid; | ||
34 | if (name2pid(name, &pid)) { | ||
35 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
36 | exit(1); | ||
37 | } | ||
38 | |||
39 | shut(pid); | ||
40 | } | ||
41 | |||
42 | void shut(pid_t pid) { | 26 | void shut(pid_t pid) { |
43 | EUID_ASSERT(); | 27 | EUID_ASSERT(); |
44 | 28 | ||
diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 539785f21..c8bed06e3 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c | |||
@@ -23,330 +23,190 @@ void usage(void) { | |||
23 | printf("firejail - version %s\n\n", VERSION); | 23 | printf("firejail - version %s\n\n", VERSION); |
24 | printf("Firejail is a SUID sandbox program that reduces the risk of security breaches by\n"); | 24 | printf("Firejail is a SUID sandbox program that reduces the risk of security breaches by\n"); |
25 | printf("restricting the running environment of untrusted applications using Linux\n"); | 25 | printf("restricting the running environment of untrusted applications using Linux\n"); |
26 | printf("namespaces. It includes a sandbox profile for Mozilla Firefox.\n\n"); | 26 | printf("namespaces.\n"); |
27 | printf("\n"); | 27 | printf("\n"); |
28 | printf("Usage: firejail [options] [program and arguments]\n\n"); | 28 | printf("Usage: firejail [options] [program and arguments]\n"); |
29 | printf("\n"); | 29 | printf("\n"); |
30 | printf("Without any options, the sandbox consists of a filesystem chroot build from the\n"); | 30 | printf("Options:\n"); |
31 | printf("current system directories mounted read-only, and new PID and IPC\n"); | 31 | printf(" -- - signal the end of options and disables further option processing.\n"); |
32 | printf("namespaces. If no program is specified as an argument, /bin/bash is started by\n"); | 32 | printf(" --allow-debuggers - allow tools such as strace and gdb inside the sandbox.\n"); |
33 | printf("default in the sandbox.\n\n"); | 33 | printf(" --allusers - all user home directories are visible inside the sandbox.\n"); |
34 | printf("\n"); | 34 | printf(" --apparmor - enable AppArmor confinement.\n"); |
35 | printf("Options:\n\n"); | 35 | printf(" --appimage - sandbox an AppImage application.\n"); |
36 | printf(" -- - signal the end of options and disables further option processing.\n\n"); | 36 | printf(" --audit[=test-program] - audit the sandbox.\n"); |
37 | #ifdef HAVE_NETWORK | 37 | #ifdef HAVE_NETWORK |
38 | printf(" --bandwidth=name|pid - set bandwidth limits for the sandbox identified\n"); | 38 | printf(" --bandwidth=name|pid - set bandwidth limits\n"); |
39 | printf("\tby name or PID, see Traffic Shaping section fo more details.\n\n"); | ||
40 | #endif | 39 | #endif |
41 | #ifdef HAVE_BIND | 40 | #ifdef HAVE_BIND |
42 | printf(" --bind=dirname1,dirname2 - mount-bind dirname1 on top of dirname2.\n\n"); | 41 | printf(" --bind=dirname1,dirname2 - mount-bind dirname1 on top of dirname2.\n"); |
43 | printf(" --bind=filename1,filename2 - mount-bind filename1 on top of filename2.\n\n"); | 42 | printf(" --bind=filename1,filename2 - mount-bind filename1 on top of filename2.\n"); |
44 | #endif | 43 | #endif |
45 | printf(" --blacklist=dirname_or_filename - blacklist directory or file.\n\n"); | 44 | printf(" --blacklist=filename - blacklist directory or file.\n"); |
46 | printf(" -c - execute command and exit.\n\n"); | 45 | printf(" -c - execute command and exit.\n"); |
47 | printf(" --caps - enable default Linux capabilities filter.\n\n"); | 46 | printf(" --caps - enable default Linux capabilities filter.\n"); |
48 | printf(" --caps.drop=all - drop all capabilities.\n\n"); | 47 | printf(" --caps.drop=all - drop all capabilities.\n"); |
49 | printf(" --caps.drop=capability,capability - blacklist capabilities filter.\n\n"); | 48 | printf(" --caps.drop=capability,capability - blacklist capabilities filter.\n"); |
50 | printf(" --caps.keep=capability,capability - whitelist capabilities filter.\n\n"); | 49 | printf(" --caps.keep=capability,capability - whitelist capabilities filter.\n"); |
51 | printf(" --caps.print=name|pid - print the caps filter for the sandbox identified\n"); | 50 | printf(" --caps.print=name|pid - print the caps filter.\n"); |
52 | printf("\tby name or PID.\n\n"); | ||
53 | printf(" --cgroup=tasks-file - place the sandbox in the specified control group.\n"); | 51 | printf(" --cgroup=tasks-file - place the sandbox in the specified control group.\n"); |
54 | printf("\ttasks-file is the full path of cgroup tasks file.\n\n"); | ||
55 | #ifdef HAVE_CHROOT | 52 | #ifdef HAVE_CHROOT |
56 | printf(" --chroot=dirname - chroot into directory.\n\n"); | 53 | printf(" --chroot=dirname - chroot into directory.\n"); |
54 | #endif | ||
55 | printf(" --cpu=cpu-number,cpu-number - set cpu affinity.\n"); | ||
56 | printf(" --cpu.print=name|pid - print the cpus in use.\n"); | ||
57 | printf(" --csh - use /bin/csh as default shell.\n"); | ||
58 | printf(" --debug - print sandbox debug messages.\n"); | ||
59 | printf(" --debug-blacklists - debug blacklisting.\n"); | ||
60 | printf(" --debug-caps - print all recognized capabilities.\n"); | ||
61 | printf(" --debug-check-filename - debug filename checking.\n"); | ||
62 | printf(" --debug-errnos - print all recognized error numbers.\n"); | ||
63 | printf(" --debug-protocols - print all recognized protocols.\n"); | ||
64 | printf(" --debug-syscalls - print all recognized system calls.\n"); | ||
65 | #ifdef HAVE_WHITELIST | ||
66 | printf(" --debug-whitelists - debug whitelisting.\n"); | ||
57 | #endif | 67 | #endif |
58 | printf(" --cpu=cpu-number,cpu-number - set cpu affinity.\n\n"); | ||
59 | printf(" --cpu.print=name|pid - print the cup in use by the sandbox identified\n"); | ||
60 | printf("\tby name or PID.\n\n"); | ||
61 | printf(" --csh - use /bin/csh as default shell.\n\n"); | ||
62 | |||
63 | printf(" --debug - print sandbox debug messages.\n\n"); | ||
64 | printf(" --debug-blacklists - debug blacklisting.\n\n"); | ||
65 | printf(" --debug-caps - print all recognized capabilities in the current Firejail\n"); | ||
66 | printf("\tsoftware build.\n\n"); | ||
67 | printf(" --debug-check-filename - debug filename checking.\n\n"); | ||
68 | printf(" --debug-errnos - print all recognized error numbers in the current Firejail\n"); | ||
69 | printf("\tsoftware build.\n\n"); | ||
70 | printf(" --debug-protocols - print all recognized protocols in the current Firejail\n"); | ||
71 | printf("\tsoftware build.\n\n"); | ||
72 | printf(" --debug-syscalls - print all recognized system calls in the current Firejail\n"); | ||
73 | printf("\tsoftware build.\n\n"); | ||
74 | printf(" --debug-whitelists - debug whitelisting.\n\n"); | ||
75 | |||
76 | |||
77 | |||
78 | #ifdef HAVE_NETWORK | 68 | #ifdef HAVE_NETWORK |
79 | printf(" --defaultgw=address - use this address as default gateway in the new network\n"); | 69 | printf(" --defaultgw=address - configure default gateway.\n"); |
80 | printf("\tnamespace.\n\n"); | ||
81 | #endif | 70 | #endif |
82 | printf(" --dns=address - set a DNS server for the sandbox. Up to three DNS servers\n"); | 71 | printf(" --dns=address - set DNS server.\n"); |
83 | printf("\tcan be defined.\n\n"); | 72 | printf(" --dns.print=name|pid - print DNS configuration.\n"); |
84 | printf(" --dns.print=name|pid - print DNS configuration for the sandbox identified\n"); | ||
85 | printf("\tby name or PID.\n\n"); | ||
86 | 73 | ||
87 | printf(" --env=name=value - set environment variable in the new sandbox.\n\n"); | 74 | printf(" --env=name=value - set environment variable.\n"); |
88 | printf(" --fs.print=name|pid - print the filesystem log for the sandbox identified\n"); | 75 | printf(" --fs.print=name|pid - print the filesystem log.\n"); |
89 | printf("\tby name or PID.\n\n"); | 76 | printf(" --get=name|pid filename - get a file from sandbox container.\n"); |
90 | printf(" --get=name|pid filename - get a file from sandbox container.\n\n"); | 77 | printf(" --help, -? - this help screen.\n"); |
91 | printf(" --help, -? - this help screen.\n\n"); | 78 | printf(" --hostname=name - set sandbox hostname.\n"); |
92 | printf(" --hostname=name - set sandbox hostname.\n\n"); | 79 | printf(" --ignore=command - ignore command in profile files.\n"); |
93 | printf(" --ignore=command - ignore command in profile files.\n\n"); | ||
94 | #ifdef HAVE_NETWORK | 80 | #ifdef HAVE_NETWORK |
95 | printf(" --interface=name - move interface in a new network namespace. Up to four\n"); | 81 | printf(" --interface=name - move interface in sandbox.\n"); |
96 | printf("\t--interface options can be specified.\n\n"); | 82 | printf(" --ip=address - set interface IP address.\n"); |
97 | printf(" --ip=address - set interface IP address.\n\n"); | 83 | printf(" --ip=none - no IP address and no default gateway are configured.\n"); |
98 | printf(" --ip=none - no IP address and no default gateway address are configured\n"); | 84 | printf(" --ip6=address - set interface IPv6 address.\n"); |
99 | printf("\tin the new network namespace. Use this option in case you intend to\n"); | 85 | printf(" --iprange=address,address - configure an IP address in this range.\n"); |
100 | printf("\tstart an external DHCP client in the sandbox.\n\n"); | ||
101 | printf(" --ip6=address - set interface IPv6 address.\n\n"); | ||
102 | printf(" --iprange=address,address - configure an IP address in this range.\n\n"); | ||
103 | #endif | 86 | #endif |
104 | printf(" --ipc-namespace - enable a new IPC namespace if the sandbox was started as\n"); | 87 | printf(" --ipc-namespace - enable a new IPC namespace.\n"); |
105 | printf("\tregular user. IPC namespace is enabled by default only if the sandbox\n"); | 88 | printf(" --join=name|pid - join the sandbox.\n"); |
106 | printf("\tis started as root.\n\n"); | 89 | printf(" --join-filesystem=name|pid - join the mount namespace.\n"); |
107 | printf(" --join=name|pid - join the sandbox identified by name or PID.\n\n"); | ||
108 | printf(" --join-filesystem=name|pid - join the mount namespace of the sandbox\n"); | ||
109 | printf("\tidentified by name or PID.\n\n"); | ||
110 | #ifdef HAVE_NETWORK | 90 | #ifdef HAVE_NETWORK |
111 | printf(" --join-network=name|pid - join the network namespace of the sandbox\n"); | 91 | printf(" --join-network=name|pid - join the network namespace.\n"); |
112 | printf("\tidentified by name or PID.\n\n"); | ||
113 | #endif | 92 | #endif |
114 | printf(" --list - list all sandboxes.\n\n"); | 93 | printf(" --list - list all sandboxes.\n"); |
115 | printf(" --ls=name|pid dir_or_filename - list files in sandbox container.\n\n"); | 94 | printf(" --ls=name|pid dir_or_filename - list files in sandbox container.\n"); |
116 | #ifdef HAVE_NETWORK | 95 | #ifdef HAVE_NETWORK |
117 | printf(" --mac=xx:xx:xx:xx:xx:xx - set interface MAC address.\n\n"); | 96 | printf(" --mac=xx:xx:xx:xx:xx:xx - set interface MAC address.\n"); |
118 | printf(" --mtu=number - set interface MTU.\n\n"); | 97 | printf(" --mtu=number - set interface MTU.\n"); |
119 | #endif | 98 | #endif |
120 | printf(" --name=name - set sandbox name.\n\n"); | 99 | printf(" --name=name - set sandbox name.\n"); |
121 | #ifdef HAVE_NETWORK | 100 | #ifdef HAVE_NETWORK |
122 | printf(" --net=bridgename - enable network namespaces and connect to this bridge\n"); | 101 | printf(" --net=bridgename - enable network namespaces and connect to this bridge.\n"); |
123 | printf("\tdevice. Up to four --net devices can be defined.\n\n"); | ||
124 | |||
125 | printf(" --net=ethernet_interface - enable network namespaces and connect to this\n"); | 102 | printf(" --net=ethernet_interface - enable network namespaces and connect to this\n"); |
126 | printf("\tEthernet interface using the standard Linux macvlan driver. Up to four\n"); | 103 | printf("\tEthernet interface.\n"); |
127 | printf("\t--net devices can be defined.\n\n"); | 104 | printf(" --net=none - enable a new, unconnected network namespace.\n"); |
128 | 105 | printf(" --netfilter[=filename] - enable the default client network filter.\n"); | |
129 | printf(" --net=none - enable a new, unconnected network namespace.\n\n"); | 106 | printf(" --netfilter6=filename - enable the IPv6 network filter.\n"); |
130 | 107 | printf(" --netstats - monitor network statistics.\n"); | |
131 | printf(" --netfilter - enable the default client network filter in the new\n"); | ||
132 | printf("\tnetwork namespace.\n\n"); | ||
133 | printf(" --netfilter=filename - enable the network filter specified by\n"); | ||
134 | printf("\tfilename in the new network namespace. The filter file format\n"); | ||
135 | printf("\tis the format of iptables-save and iptable-restore commands.\n\n"); | ||
136 | printf(" --netfilter6=filename - enable the IPv6 network filter specified by\n"); | ||
137 | printf("\tfilename in the new network namespace. The filter file format\n"); | ||
138 | printf("\tis the format of ip6tables-save and ip6table-restore commands.\n\n"); | ||
139 | |||
140 | printf(" --netstats - monitor network statistics for sandboxes creating a new\n"); | ||
141 | printf("\tnetwork namespace.\n\n"); | ||
142 | #endif | 108 | #endif |
143 | printf(" --nice=value - set nice value\n\n"); | 109 | printf(" --nice=value - set nice value.\n"); |
144 | printf(" --noblacklist=dirname_or_filename - disable blacklist for directory or\n"); | 110 | printf(" --no3d - disable 3D hardware acceleration.\n"); |
145 | printf("\tfile.\n\n"); | 111 | printf(" --noblacklist=filename - disable blacklist for file or directory .\n"); |
146 | printf(" --nogroups - disable supplementary groups. Without this option,\n"); | 112 | printf(" --noexec=filename - remount the file or directory noexec nosuid and nodev.\n"); |
147 | printf("\tsupplementary groups are enabled for the user starting the sandbox.\n"); | 113 | printf(" --nogroups - disable supplementary groups.\n"); |
148 | printf("\t For root, groups are always disabled.\n\n"); | 114 | printf(" --noprofile - do not use a security profile.\n"); |
149 | |||
150 | printf(" --noprofile - do not use a profile. Profile priority is use the one\n"); | ||
151 | printf("\tspecified on the command line, next try to find one that\n"); | ||
152 | printf("\tmatches the command name, and lastly use %s.profile\n", DEFAULT_USER_PROFILE); | ||
153 | printf("\tif running as regular user or %s.profile if running as\n", DEFAULT_ROOT_PROFILE); | ||
154 | printf("\troot.\n\n"); | ||
155 | #ifdef HAVE_USERNS | 115 | #ifdef HAVE_USERNS |
156 | printf(" --noroot - install a user namespace with a single user - the current\n"); | 116 | printf(" --noroot - install a user namespace with only the current user.\n"); |
157 | printf("\tuser. root user does not exist in the new namespace. This option\n"); | ||
158 | printf("\tis not supported for --chroot and --overlay configurations.\n\n"); | ||
159 | #endif | 117 | #endif |
160 | printf(" --nosound - disable sound system.\n\n"); | 118 | printf(" --nonewprivs - sets the NO_NEW_PRIVS prctl.\n"); |
161 | 119 | printf(" --output=logfile - stdout logging and log rotation.\n"); | |
162 | printf(" --output=logfile - stdout logging and log rotation. Copy stdout and stderr\n"); | ||
163 | printf("\tto logfile, and keep the size of the file under 500KB using log\n"); | ||
164 | printf("\trotation. Five files with prefixes .1 to .5 are used in\n"); | ||
165 | printf("\trotation.\n\n"); | ||
166 | |||
167 | printf(" --overlay - mount a filesystem overlay on top of the current filesystem.\n"); | 120 | printf(" --overlay - mount a filesystem overlay on top of the current filesystem.\n"); |
168 | printf("\tThe upper filesystem layer is persistent, and stored in\n"); | 121 | printf(" --overlay-named=name - mount a filesystem overlay on top of the current\n"); |
169 | printf("\t$HOME/.firejail directory. (OverlayFS support is required in\n"); | 122 | printf("\tfilesystem, and store it in name directory.\n"); |
170 | printf("\tLinux kernel for this option to work). \n\n"); | 123 | printf(" --overlay-tmpfs - mount a temporary filesystem overlay on top of the current\n"); |
171 | 124 | printf("\tfilesystem.\n"); | |
172 | printf(" --overlay-tmpfs - mount a filesystem overlay on top of the current\n"); | 125 | printf(" --overlay-clean - clean all overlays stored in $HOME/.firejail directory.\n"); |
173 | printf("\tfilesystem. The upper layer is stored in a tmpfs filesystem,\n"); | 126 | printf(" --private - temporary home directory.\n"); |
174 | printf("\tand it is discarded when the sandbox is closed. (OverlayFS\n"); | 127 | printf(" --private=directory - use directory as user home.\n"); |
175 | printf("\tsupport is required in Linux kernel for this option to work).\n\n"); | 128 | printf(" --private-home=file,directory - build a new user home in a temporary\n"); |
176 | 129 | printf("\tfilesystem, and copy the files and directories in the list in\n"); | |
177 | printf(" --private - mount new /root and /home/user directories in temporary\n"); | 130 | printf("\tthe new home.\n"); |
178 | printf("\tfilesystems. All modifications are discarded when the sandbox is\n"); | ||
179 | printf("\tclosed.\n\n"); | ||
180 | printf(" --private=directory - use directory as user home.\n\n"); | ||
181 | |||
182 | printf(" --private-bin=file,file - build a new /bin in a temporary filesystem,\n"); | 131 | printf(" --private-bin=file,file - build a new /bin in a temporary filesystem,\n"); |
183 | printf("\tand copy the programs in the list.\n\n"); | 132 | printf("\tand copy the programs in the list.\n"); |
184 | |||
185 | printf(" --private-dev - create a new /dev directory. Only dri, null, full, zero,\n"); | 133 | printf(" --private-dev - create a new /dev directory. Only dri, null, full, zero,\n"); |
186 | printf("\ttty, pst, ptms, random, urandom, log and shm devices are available.\n\n"); | 134 | printf("\ttty, pst, ptms, random, snd, urandom, log and shm devices are available.\n"); |
187 | |||
188 | printf(" --private-etc=file,directory - build a new /etc in a temporary\n"); | 135 | printf(" --private-etc=file,directory - build a new /etc in a temporary\n"); |
189 | printf("\tfilesystem, and copy the files and directories in the list.\n"); | 136 | printf("\tfilesystem, and copy the files and directories in the list.\n"); |
190 | printf("\tAll modifications are discarded when the sandbox is closed.\n\n"); | 137 | printf(" --private-tmp - mount a tmpfs on top of /tmp directory.\n"); |
191 | 138 | printf(" --profile=filename - use a custom profile.\n"); | |
192 | printf(" --private-tmp - mount a tmpfs on top of /tmp directory\n\n"); | 139 | printf(" --profile-path=directory - use this directory to look for profile files.\n"); |
193 | |||
194 | printf(" --profile=filename - use a custom profile.\n\n"); | ||
195 | printf(" --profile-path=directory - use this directory to look for profile files.\n\n"); | ||
196 | |||
197 | printf(" --protocol=protocol,protocol,protocol - enable protocol filter.\n"); | 140 | printf(" --protocol=protocol,protocol,protocol - enable protocol filter.\n"); |
198 | printf("\tProtocol values: unix, inet, inet6, netlink, packet.\n\n"); | 141 | printf(" --protocol.print=name|pid - print the protocol filter.\n"); |
199 | printf(" --protocol.print=name|pid - print the protocol filter for the sandbox\n"); | 142 | printf(" --put=name|pid src-filename dest-filename - put a file in sandbox container.\n"); |
200 | printf("\tidentified by name or PID.\n\n"); | 143 | printf(" --quiet - turn off Firejail's output.\n"); |
201 | 144 | printf(" --read-only=filename - set directory or file read-only..\n"); | |
202 | printf(" --quiet - turn off Firejail's output.\n\n"); | 145 | printf(" --read-write=filename - set directory or file read-write..\n"); |
203 | printf(" --read-only=dirname_or_filename - set directory or file read-only..\n\n"); | ||
204 | printf(" --rlimit-fsize=number - set the maximum file size that can be created\n"); | 146 | printf(" --rlimit-fsize=number - set the maximum file size that can be created\n"); |
205 | printf("\tby a process.\n\n"); | 147 | printf("\tby a process.\n"); |
206 | printf(" --rlimit-nofile=number - set the maximum number of files that can be\n"); | 148 | printf(" --rlimit-nofile=number - set the maximum number of files that can be\n"); |
207 | printf("\topened by a process.\n\n"); | 149 | printf("\topened by a process.\n"); |
208 | printf(" --rlimit-nproc=number - set the maximum number of processes that can be\n"); | 150 | printf(" --rlimit-nproc=number - set the maximum number of processes that can be\n"); |
209 | printf("\tcreated for the real user ID of the calling process.\n\n"); | 151 | printf("\tcreated for the real user ID of the calling process.\n"); |
210 | printf(" --rlimit-sigpending=number - set the maximum number of pending signals\n"); | 152 | printf(" --rlimit-sigpending=number - set the maximum number of pending signals\n"); |
211 | printf("\tfor a process.\n\n"); | 153 | printf("\tfor a process.\n"); |
154 | printf(" --rmenv=name - remove environment variable in the new sandbox.\n"); | ||
212 | #ifdef HAVE_NETWORK | 155 | #ifdef HAVE_NETWORK |
213 | printf(" --scan - ARP-scan all the networks from inside a network namespace.\n"); | 156 | printf(" --scan - ARP-scan all the networks from inside a network namespace.\n"); |
214 | printf("\tThis makes it possible to detect macvlan kernel device drivers\n"); | ||
215 | printf("\trunning on the current host.\n\n"); | ||
216 | #endif | 157 | #endif |
217 | #ifdef HAVE_SECCOMP | 158 | #ifdef HAVE_SECCOMP |
218 | printf(" --seccomp - enable seccomp filter and apply the default blacklist.\n\n"); | 159 | printf(" --seccomp - enable seccomp filter and apply the default blacklist.\n"); |
219 | |||
220 | printf(" --seccomp=syscall,syscall,syscall - enable seccomp filter, blacklist the\n"); | 160 | printf(" --seccomp=syscall,syscall,syscall - enable seccomp filter, blacklist the\n"); |
221 | printf("\tdefault syscall list and the syscalls specified by the command.\n\n"); | 161 | printf("\tdefault syscall list and the syscalls specified by the command.\n"); |
222 | |||
223 | printf(" --seccomp.drop=syscall,syscall,syscall - enable seccomp filter, and\n"); | 162 | printf(" --seccomp.drop=syscall,syscall,syscall - enable seccomp filter, and\n"); |
224 | printf("\tblacklist the syscalls specified by the command.\n\n"); | 163 | printf("\tblacklist the syscalls specified by the command.\n"); |
225 | |||
226 | printf(" --seccomp.keep=syscall,syscall,syscall - enable seccomp filter, and\n"); | 164 | printf(" --seccomp.keep=syscall,syscall,syscall - enable seccomp filter, and\n"); |
227 | printf("\twhitelist the syscalls specified by the command.\n\n"); | 165 | printf("\twhitelist the syscalls specified by the command.\n"); |
228 | |||
229 | printf(" --seccomp.<errno>=syscall,syscall,syscall - enable seccomp filter, and\n"); | 166 | printf(" --seccomp.<errno>=syscall,syscall,syscall - enable seccomp filter, and\n"); |
230 | printf("\treturn errno for the syscalls specified by the command.\n\n"); | 167 | printf("\treturn errno for the syscalls specified by the command.\n"); |
231 | |||
232 | printf(" --seccomp.print=name|pid - print the seccomp filter for the sandbox\n"); | 168 | printf(" --seccomp.print=name|pid - print the seccomp filter for the sandbox\n"); |
233 | printf("\tidentified by name or PID.\n\n"); | 169 | printf("\tidentified by name or PID.\n"); |
234 | #endif | 170 | #endif |
235 | 171 | printf(" --shell=none - run the program directly without a user shell.\n"); | |
236 | printf(" --shell=none - run the program directly without a user shell.\n\n"); | 172 | printf(" --shell=program - set default user shell.\n"); |
237 | printf(" --shell=program - set default user shell.\n\n"); | 173 | printf(" --shutdown=name|pid - shutdown the sandbox identified by name or PID.\n"); |
238 | printf(" --shutdown=name|pid - shutdown the sandbox identified by name or PID.\n\n"); | ||
239 | printf(" --tmpfs=dirname - mount a tmpfs filesystem on directory dirname.\n"); | 174 | printf(" --tmpfs=dirname - mount a tmpfs filesystem on directory dirname.\n"); |
240 | printf("\tThis option is available only when running the sandbox as root.\n\n"); | 175 | printf(" --top - monitor the most CPU-intensive sandboxes.\n"); |
241 | printf(" --top - monitor the most CPU-intensive sandboxes.\n\n"); | 176 | printf(" --trace - trace open, access and connect system calls.\n"); |
242 | printf(" --trace - trace open, access and connect system calls.\n\n"); | ||
243 | printf(" --tracelog - add a syslog message for every access to files or\n"); | 177 | printf(" --tracelog - add a syslog message for every access to files or\n"); |
244 | printf("\tdirectoires blacklisted by the security profile.\n\n"); | 178 | printf("\tdirectoires blacklisted by the security profile.\n"); |
245 | printf(" --tree - print a tree of all sandboxed processes.\n\n"); | 179 | printf(" --tree - print a tree of all sandboxed processes.\n"); |
246 | printf(" --user=new_user - switch the user before starting the sandbox.\n\n"); | 180 | printf(" --version - print program version and exit.\n"); |
247 | printf(" --version - print program version and exit.\n\n"); | ||
248 | printf(" --whitelist=dirname_or_filename - whitelist directory or file.\n\n"); | ||
249 | printf(" --x11 - enable X11 server. The software checks first if Xpra is installed,\n"); | ||
250 | printf("\tthen it checks if Xephyr is installed.\n\n"); | ||
251 | printf(" --x11=xpra - enable Xpra X11 server.\n\n"); | ||
252 | printf(" --x11=xephyr - enable Xephyr X11 server. The window size is 800x600.\n\n"); | ||
253 | printf(" --zsh - use /usr/bin/zsh as default shell.\n\n"); | ||
254 | printf("\n"); | ||
255 | printf("\n"); | ||
256 | |||
257 | |||
258 | #ifdef HAVE_NETWORK | 181 | #ifdef HAVE_NETWORK |
259 | printf("Traffic Shaping\n\n"); | 182 | printf(" --veth-name=name - use this name for the interface connected to the bridge.\n"); |
260 | 183 | #endif | |
261 | printf("Network bandwidth is an expensive resource shared among all sandboxes\n"); | 184 | #ifdef HAVE_WHITELIST |
262 | printf("running on a system. Traffic shaping allows the user to increase network\n"); | 185 | printf(" --whitelist=filename - whitelist directory or file.\n"); |
263 | printf("performance by controlling the amount of data that flows into and out of the\n"); | 186 | #endif |
264 | printf("sandboxes. Firejail implements a simple rate-limiting shaper based on Linux\n"); | 187 | printf(" --writable-etc - /etc directory is mounted read-write.\n"); |
265 | printf("command tc. The shaper works at sandbox level, and can be used only for\n"); | 188 | printf(" --writable-var - /var directory is mounted read-write.\n"); |
266 | printf("sandboxes configured with new network namespaces.\n\n"); | 189 | printf(" --x11 - enable X11 sandboxing. The software checks first if Xpra is\n"); |
267 | 190 | printf("\tinstalled, then it checks if Xephyr is installed. If all fails, it will\n"); | |
268 | printf("Set rate-limits:\n"); | 191 | printf("\tattempt to use X11 security extension.\n"); |
269 | printf(" firejail --bandwidth={name|pid} set network-name down-speed up-speed\n\n"); | 192 | printf(" --x11=none - disable access to X11 sockets.\n"); |
270 | printf("Clear rate-limits:\n"); | 193 | printf(" --x11=xephyr - enable Xephyr X11 server. The window size is 800x600.\n"); |
271 | printf(" firejail --bandwidth={name|pid} clear network-name\n\n"); | 194 | printf(" --x11=xorg - enable X11 security extension.\n"); |
272 | printf("Status:\n"); | 195 | printf(" --x11=xpra - enable Xpra X11 server.\n"); |
273 | printf(" firejail --bandwidth={name|pid} status\n\n"); | 196 | printf(" --zsh - use /usr/bin/zsh as default shell.\n"); |
274 | printf("where:\n"); | ||
275 | printf(" name - sandbox name\n"); | ||
276 | printf(" pid - sandbox pid\n"); | ||
277 | printf(" network-name - network name as used by --net option\n"); | ||
278 | printf(" down-speed - download speed in KB/s (decimal kilobyte per second)\n"); | ||
279 | printf(" up-speed - upload speed in KB/s (decimal kilobyte per second)\n"); | ||
280 | printf("\n"); | ||
281 | printf("Example:\n"); | ||
282 | printf(" $ firejail --name=mybrowser --net=eth0 firefox &\n"); | ||
283 | printf(" $ firejail --bandwidth=mybrowser set eth0 80 20\n"); | ||
284 | printf(" $ firejail --bandwidth=mybrowser status\n"); | ||
285 | printf(" $ firejail --bandwidth=mybrowser clear eth0\n"); | ||
286 | printf("\n"); | ||
287 | printf("\n"); | ||
288 | #endif | ||
289 | |||
290 | |||
291 | printf("Monitoring\n\n"); | ||
292 | |||
293 | printf("Option --list prints a list of all sandboxes. The format for each entry is as\n"); | ||
294 | printf("follows:\n\n"); | ||
295 | printf(" PID:USER:Command\n\n"); | ||
296 | |||
297 | printf("Option --tree prints the tree of processes running in the sandbox. The format\n"); | ||
298 | printf("for each process entry is as follows:\n\n"); | ||
299 | printf(" PID:USER:Command\n\n"); | ||
300 | |||
301 | printf("Option --top is similar to the UNIX top command, however it applies only to\n"); | ||
302 | printf("sandboxes. Listed below are the available fields (columns) in alphabetical\n"); | ||
303 | printf("order:\n\n"); | ||
304 | printf(" Command - command used to start the sandbox.\n"); | ||
305 | printf(" CPU%% - CPU usage, the sandbox share of the elapsed CPU time since the\n"); | ||
306 | printf("\tlast screen update\n"); | ||
307 | printf(" PID - Unique process ID for the task controlling the sandbox.\n"); | ||
308 | printf(" Prcs - number of processes running in sandbox, including the controlling\n"); | ||
309 | printf("\tprocess.\n"); | ||
310 | printf(" RES - Resident Memory Size (KiB), sandbox non-swapped physical memory.\n"); | ||
311 | printf("\tIt is a sum of the RES values for all processes running in the\n"); | ||
312 | printf("\tsandbox.\n"); | ||
313 | printf(" SHR - Shared Memory Size (KiB), it reflects memory shared with other\n"); | ||
314 | printf("\tprocesses. It is a sum of the SHR values for all processes running\n"); | ||
315 | printf("\tin the sandbox, including the controlling process.\n"); | ||
316 | printf(" Uptime - sandbox running time in hours:minutes:seconds format.\n"); | ||
317 | printf(" User - The owner of the sandbox.\n"); | ||
318 | printf("\n"); | ||
319 | printf("\n"); | ||
320 | printf("Profile files\n\n"); | ||
321 | printf("Several command line configuration options can be passed to the program using\n"); | ||
322 | printf("profile files. Default Firejail profile files are stored in /etc/firejail\n"); | ||
323 | printf("directory, user profile files are stored in ~/.config/firejail directory. See\n"); | ||
324 | printf("man 5 firejail-profile for more information.\n\n"); | ||
325 | printf("\n"); | ||
326 | printf("Restricted shell\n\n"); | ||
327 | printf("To configure a restricted shell, replace /bin/bash with /usr/bin/firejail in\n"); | ||
328 | printf("/etc/password file for each user that needs to be restricted.\n"); | ||
329 | printf("Alternatively, you can specify /usr/bin/firejail in adduser command:\n\n"); | ||
330 | printf(" adduser --shell /usr/bin/firejail username\n\n"); | ||
331 | printf("Arguments to be passed to firejail executable upon login are declared in\n"); | ||
332 | printf("/etc/firejail/login.users file.\n\n"); | ||
333 | printf("\n"); | 197 | printf("\n"); |
334 | printf("Examples:\n\n"); | 198 | printf("Examples:\n"); |
335 | printf(" $ firejail\n"); | ||
336 | printf("\tstart a regular /bin/bash session in sandbox\n"); | ||
337 | printf(" $ firejail firefox\n"); | 199 | printf(" $ firejail firefox\n"); |
338 | printf("\tstart Mozilla Firefox\n"); | 200 | printf("\tstart Mozilla Firefox\n"); |
339 | printf(" $ firejail --debug firefox\n"); | 201 | printf(" $ firejail --debug firefox\n"); |
340 | printf("\tdebug Firefox sandbox\n"); | 202 | printf("\tdebug Firefox sandbox\n"); |
341 | printf(" $ firejail --private firefox\n"); | 203 | printf(" $ firejail --private --sna=8.8.8.8 firefox\n"); |
342 | printf("\tstart Firefox with a new, empty home directory\n"); | 204 | printf("\tstart Firefox with a new, empty home directory, and a well-known DNS\n"); |
343 | printf(" $ firejail --net=br0 ip=10.10.20.10\n"); | 205 | printf("\tserver setting.\n"); |
344 | printf("\tstart a /bin/bash session in a new network namespace; the session is\n"); | 206 | printf(" $ firejail --net=eth0 firefox\n"); |
345 | printf("\tconnected to the main network using br0 bridge device, an IP address\n"); | 207 | printf("\tstart Firefox in a new network namespace\n"); |
346 | printf("\tof 10.10.20.10 is assigned to the sandbox\n"); | 208 | printf(" $ firejail --x11=xorg firefox\n"); |
347 | printf(" $ firejail --net=br0 --net=br1 --net=br2\n"); | 209 | printf("\tstart Firefox and sandbox X11\n"); |
348 | printf("\tstart a /bin/bash session in a new network namespace and connect it\n"); | ||
349 | printf("\tto br0, br1, and br2 host bridge devices\n"); | ||
350 | printf(" $ firejail --list\n"); | 210 | printf(" $ firejail --list\n"); |
351 | printf("\tlist all running sandboxes\n"); | 211 | printf("\tlist all running sandboxes\n"); |
352 | printf("\n"); | 212 | printf("\n"); |
diff --git a/src/firejail/user.c b/src/firejail/user.c deleted file mode 100644 index a2f34392c..000000000 --- a/src/firejail/user.c +++ /dev/null | |||
@@ -1,115 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "firejail.h" | ||
21 | #include <sys/types.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <unistd.h> | ||
24 | #include <grp.h> | ||
25 | #include <pwd.h> | ||
26 | |||
27 | |||
28 | void check_user(int argc, char **argv) { | ||
29 | EUID_ASSERT(); | ||
30 | int i; | ||
31 | char *user = NULL; | ||
32 | |||
33 | int found = 0; | ||
34 | for (i = 1; i < argc; i++) { | ||
35 | // check options | ||
36 | if (strcmp(argv[i], "--") == 0) | ||
37 | break; | ||
38 | if (strncmp(argv[i], "--", 2) != 0) | ||
39 | break; | ||
40 | |||
41 | // check user option | ||
42 | if (strncmp(argv[i], "--user=", 7) == 0) { | ||
43 | found = 1; | ||
44 | user = argv[i] + 7; | ||
45 | break; | ||
46 | } | ||
47 | } | ||
48 | if (!found) | ||
49 | return; | ||
50 | |||
51 | // check root | ||
52 | if (getuid() != 0) { | ||
53 | fprintf(stderr, "Error: you need to be root to use --user command line option\n"); | ||
54 | exit(1); | ||
55 | } | ||
56 | |||
57 | // switch user | ||
58 | struct passwd *pw = getpwnam(user); | ||
59 | if (!pw) { | ||
60 | fprintf(stderr, "Error: cannot find user %s\n", user); | ||
61 | exit(1); | ||
62 | } | ||
63 | |||
64 | printf("Switching to user %s, UID %d, GID %d\n", user, pw->pw_uid, pw->pw_gid); | ||
65 | int rv = initgroups(user, pw->pw_gid); | ||
66 | if (rv == -1) { | ||
67 | perror("initgroups"); | ||
68 | fprintf(stderr, "Error: cannot switch to user %s\n", user); | ||
69 | } | ||
70 | |||
71 | rv = setgid(pw->pw_gid); | ||
72 | if (rv == -1) { | ||
73 | perror("setgid"); | ||
74 | fprintf(stderr, "Error: cannot switch to user %s\n", user); | ||
75 | } | ||
76 | |||
77 | rv = setuid(pw->pw_uid); | ||
78 | if (rv == -1) { | ||
79 | perror("setuid"); | ||
80 | fprintf(stderr, "Error: cannot switch to user %s\n", user); | ||
81 | } | ||
82 | |||
83 | // build the new command line | ||
84 | int len = 0; | ||
85 | for (i = 0; i < argc; i++) { | ||
86 | len += strlen(argv[i]) + 1; // + ' ' | ||
87 | } | ||
88 | |||
89 | char *cmd = malloc(len + 1); // + '\0' | ||
90 | if (!cmd) | ||
91 | errExit("malloc"); | ||
92 | |||
93 | char *ptr = cmd; | ||
94 | int first = 1; | ||
95 | for (i = 0; i < argc; i++) { | ||
96 | if (strncmp(argv[i], "--user=", 7) == 0 && first) { | ||
97 | first = 0; | ||
98 | continue; | ||
99 | } | ||
100 | |||
101 | ptr += sprintf(ptr, "%s ", argv[i]); | ||
102 | } | ||
103 | |||
104 | // run command | ||
105 | char *a[4]; | ||
106 | a[0] = "/bin/bash"; | ||
107 | a[1] = "-c"; | ||
108 | a[2] = cmd; | ||
109 | a[3] = NULL; | ||
110 | |||
111 | execvp(a[0], a); | ||
112 | |||
113 | perror("execvp"); | ||
114 | exit(1); | ||
115 | } | ||
diff --git a/src/firejail/util.c b/src/firejail/util.c index da73bbfd5..d928c6b42 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -17,18 +17,23 @@ | |||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | 17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | #define _XOPEN_SOURCE 500 | ||
20 | #include "firejail.h" | 21 | #include "firejail.h" |
22 | #include <ftw.h> | ||
21 | #include <sys/stat.h> | 23 | #include <sys/stat.h> |
22 | #include <fcntl.h> | 24 | #include <fcntl.h> |
23 | #include <syslog.h> | 25 | #include <syslog.h> |
24 | #include <errno.h> | 26 | #include <errno.h> |
25 | #include <dirent.h> | 27 | #include <dirent.h> |
26 | #include <grp.h> | 28 | #include <grp.h> |
29 | #include <sys/ioctl.h> | ||
30 | #include <termios.h> | ||
27 | 31 | ||
28 | #define MAX_GROUPS 1024 | 32 | #define MAX_GROUPS 1024 |
29 | // drop privileges | 33 | // drop privileges |
30 | // - for root group or if nogroups is set, supplementary groups are not configured | 34 | // - for root group or if nogroups is set, supplementary groups are not configured |
31 | void drop_privs(int nogroups) { | 35 | void drop_privs(int nogroups) { |
36 | EUID_ROOT(); | ||
32 | gid_t gid = getgid(); | 37 | gid_t gid = getgid(); |
33 | 38 | ||
34 | // configure supplementary groups | 39 | // configure supplementary groups |
@@ -77,7 +82,7 @@ void drop_privs(int nogroups) { | |||
77 | 82 | ||
78 | int mkpath_as_root(const char* path) { | 83 | int mkpath_as_root(const char* path) { |
79 | assert(path && *path); | 84 | assert(path && *path); |
80 | 85 | ||
81 | // work on a copy of the path | 86 | // work on a copy of the path |
82 | char *file_path = strdup(path); | 87 | char *file_path = strdup(path); |
83 | if (!file_path) | 88 | if (!file_path) |
@@ -95,24 +100,21 @@ int mkpath_as_root(const char* path) { | |||
95 | } | 100 | } |
96 | } | 101 | } |
97 | else { | 102 | else { |
98 | if (chmod(file_path, 0755) == -1) | 103 | if (set_perms(file_path, 0, 0, 0755)) |
99 | errExit("chmod"); | 104 | errExit("set_perms"); |
100 | if (chown(file_path, 0, 0) == -1) | ||
101 | errExit("chown"); | ||
102 | done = 1; | 105 | done = 1; |
103 | } | 106 | } |
104 | 107 | ||
105 | *p='/'; | 108 | *p='/'; |
106 | } | 109 | } |
107 | if (done) | 110 | if (done) |
108 | fs_logger2("mkpath", path); | 111 | fs_logger2("mkpath", path); |
109 | 112 | ||
110 | free(file_path); | 113 | free(file_path); |
111 | return 0; | 114 | return 0; |
112 | } | 115 | } |
113 | 116 | ||
114 | 117 | ||
115 | |||
116 | void logsignal(int s) { | 118 | void logsignal(int s) { |
117 | if (!arg_debug) | 119 | if (!arg_debug) |
118 | return; | 120 | return; |
@@ -167,8 +169,8 @@ void logerr(const char *msg) { | |||
167 | } | 169 | } |
168 | 170 | ||
169 | 171 | ||
170 | // return -1 if error, 0 if no error | 172 | // return -1 if error, 0 if no error; if destname already exists, return error |
171 | int copy_file(const char *srcname, const char *destname) { | 173 | int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) { |
172 | assert(srcname); | 174 | assert(srcname); |
173 | assert(destname); | 175 | assert(destname); |
174 | 176 | ||
@@ -205,17 +207,23 @@ int copy_file(const char *srcname, const char *destname) { | |||
205 | } | 207 | } |
206 | } | 208 | } |
207 | 209 | ||
210 | if (fchown(dst, uid, gid) == -1) | ||
211 | errExit("fchown"); | ||
212 | if (fchmod(dst, mode) == -1) | ||
213 | errExit("fchmod"); | ||
214 | |||
208 | close(src); | 215 | close(src); |
209 | close(dst); | 216 | close(dst); |
210 | return 0; | 217 | return 0; |
211 | } | 218 | } |
212 | 219 | ||
220 | |||
213 | // return 1 if the file is a directory | 221 | // return 1 if the file is a directory |
214 | int is_dir(const char *fname) { | 222 | int is_dir(const char *fname) { |
215 | assert(fname); | 223 | assert(fname); |
216 | if (*fname == '\0') | 224 | if (*fname == '\0') |
217 | return 0; | 225 | return 0; |
218 | 226 | ||
219 | // if fname doesn't end in '/', add one | 227 | // if fname doesn't end in '/', add one |
220 | int rv; | 228 | int rv; |
221 | struct stat s; | 229 | struct stat s; |
@@ -226,20 +234,21 @@ int is_dir(const char *fname) { | |||
226 | if (asprintf(&tmp, "%s/", fname) == -1) { | 234 | if (asprintf(&tmp, "%s/", fname) == -1) { |
227 | fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__); | 235 | fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__); |
228 | errExit("asprintf"); | 236 | errExit("asprintf"); |
229 | } | 237 | } |
230 | rv = stat(tmp, &s); | 238 | rv = stat(tmp, &s); |
231 | free(tmp); | 239 | free(tmp); |
232 | } | 240 | } |
233 | 241 | ||
234 | if (rv == -1) | 242 | if (rv == -1) |
235 | return 0; | 243 | return 0; |
236 | 244 | ||
237 | if (S_ISDIR(s.st_mode)) | 245 | if (S_ISDIR(s.st_mode)) |
238 | return 1; | 246 | return 1; |
239 | 247 | ||
240 | return 0; | 248 | return 0; |
241 | } | 249 | } |
242 | 250 | ||
251 | |||
243 | // return 1 if the file is a link | 252 | // return 1 if the file is a link |
244 | int is_link(const char *fname) { | 253 | int is_link(const char *fname) { |
245 | assert(fname); | 254 | assert(fname); |
@@ -324,7 +333,7 @@ char *split_comma(char *str) { | |||
324 | 333 | ||
325 | int not_unsigned(const char *str) { | 334 | int not_unsigned(const char *str) { |
326 | EUID_ASSERT(); | 335 | EUID_ASSERT(); |
327 | 336 | ||
328 | int rv = 0; | 337 | int rv = 0; |
329 | const char *ptr = str; | 338 | const char *ptr = str; |
330 | while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') { | 339 | while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') { |
@@ -346,7 +355,7 @@ int find_child(pid_t parent, pid_t *child) { | |||
346 | *child = 0; // use it to flag a found child | 355 | *child = 0; // use it to flag a found child |
347 | 356 | ||
348 | DIR *dir; | 357 | DIR *dir; |
349 | EUID_ROOT(); // grsecurity fix | 358 | EUID_ROOT(); // grsecurity fix |
350 | if (!(dir = opendir("/proc"))) { | 359 | if (!(dir = opendir("/proc"))) { |
351 | // sleep 2 seconds and try again | 360 | // sleep 2 seconds and try again |
352 | sleep(2); | 361 | sleep(2); |
@@ -403,13 +412,11 @@ int find_child(pid_t parent, pid_t *child) { | |||
403 | } | 412 | } |
404 | 413 | ||
405 | 414 | ||
406 | |||
407 | void extract_command_name(int index, char **argv) { | 415 | void extract_command_name(int index, char **argv) { |
408 | EUID_ASSERT(); | 416 | EUID_ASSERT(); |
409 | assert(argv); | 417 | assert(argv); |
410 | assert(argv[index]); | 418 | assert(argv[index]); |
411 | 419 | ||
412 | |||
413 | // configure command index | 420 | // configure command index |
414 | cfg.original_program_index = index; | 421 | cfg.original_program_index = index; |
415 | 422 | ||
@@ -418,13 +425,13 @@ void extract_command_name(int index, char **argv) { | |||
418 | errExit("strdup"); | 425 | errExit("strdup"); |
419 | 426 | ||
420 | // if we have a symbolic link, use the real path to extract the name | 427 | // if we have a symbolic link, use the real path to extract the name |
421 | if (is_link(argv[index])) { | 428 | // if (is_link(argv[index])) { |
422 | char*newname = realpath(argv[index], NULL); | 429 | // char*newname = realpath(argv[index], NULL); |
423 | if (newname) { | 430 | // if (newname) { |
424 | free(str); | 431 | // free(str); |
425 | str = newname; | 432 | // str = newname; |
426 | } | 433 | // } |
427 | } | 434 | // } |
428 | 435 | ||
429 | // configure command name | 436 | // configure command name |
430 | cfg.command_name = str; | 437 | cfg.command_name = str; |
@@ -446,7 +453,6 @@ void extract_command_name(int index, char **argv) { | |||
446 | exit(1); | 453 | exit(1); |
447 | } | 454 | } |
448 | 455 | ||
449 | |||
450 | char *tmp = strdup(ptr); | 456 | char *tmp = strdup(ptr); |
451 | if (!tmp) | 457 | if (!tmp) |
452 | errExit("strdup"); | 458 | errExit("strdup"); |
@@ -532,6 +538,7 @@ void notify_other(int fd) { | |||
532 | fclose(stream); | 538 | fclose(stream); |
533 | } | 539 | } |
534 | 540 | ||
541 | |||
535 | // This function takes a pathname supplied by the user and expands '~' and | 542 | // This function takes a pathname supplied by the user and expands '~' and |
536 | // '${HOME}' at the start, to refer to a path relative to the user's home | 543 | // '${HOME}' at the start, to refer to a path relative to the user's home |
537 | // directory (supplied). | 544 | // directory (supplied). |
@@ -540,7 +547,7 @@ void notify_other(int fd) { | |||
540 | char *expand_home(const char *path, const char* homedir) { | 547 | char *expand_home(const char *path, const char* homedir) { |
541 | assert(path); | 548 | assert(path); |
542 | assert(homedir); | 549 | assert(homedir); |
543 | 550 | ||
544 | // Replace home macro | 551 | // Replace home macro |
545 | char *new_name = NULL; | 552 | char *new_name = NULL; |
546 | if (strncmp(path, "${HOME}", 7) == 0) { | 553 | if (strncmp(path, "${HOME}", 7) == 0) { |
@@ -548,15 +555,16 @@ char *expand_home(const char *path, const char* homedir) { | |||
548 | errExit("asprintf"); | 555 | errExit("asprintf"); |
549 | return new_name; | 556 | return new_name; |
550 | } | 557 | } |
551 | else if (strncmp(path, "~/", 2) == 0) { | 558 | else if (*path == '~') { |
552 | if (asprintf(&new_name, "%s%s", homedir, path + 1) == -1) | 559 | if (asprintf(&new_name, "%s%s", homedir, path + 1) == -1) |
553 | errExit("asprintf"); | 560 | errExit("asprintf"); |
554 | return new_name; | 561 | return new_name; |
555 | } | 562 | } |
556 | 563 | ||
557 | return strdup(path); | 564 | return strdup(path); |
558 | } | 565 | } |
559 | 566 | ||
567 | |||
560 | // Equivalent to the GNU version of basename, which is incompatible with | 568 | // Equivalent to the GNU version of basename, which is incompatible with |
561 | // the POSIX basename. A few lines of code saves any portability pain. | 569 | // the POSIX basename. A few lines of code saves any portability pain. |
562 | // https://www.gnu.org/software/libc/manual/html_node/Finding-Tokens-in-a-String.html#index-basename | 570 | // https://www.gnu.org/software/libc/manual/html_node/Finding-Tokens-in-a-String.html#index-basename |
@@ -567,17 +575,18 @@ const char *gnu_basename(const char *path) { | |||
567 | return last_slash+1; | 575 | return last_slash+1; |
568 | } | 576 | } |
569 | 577 | ||
578 | |||
570 | uid_t pid_get_uid(pid_t pid) { | 579 | uid_t pid_get_uid(pid_t pid) { |
571 | EUID_ASSERT(); | 580 | EUID_ASSERT(); |
572 | uid_t rv = 0; | 581 | uid_t rv = 0; |
573 | 582 | ||
574 | // open status file | 583 | // open status file |
575 | char *file; | 584 | char *file; |
576 | if (asprintf(&file, "/proc/%u/status", pid) == -1) { | 585 | if (asprintf(&file, "/proc/%u/status", pid) == -1) { |
577 | perror("asprintf"); | 586 | perror("asprintf"); |
578 | exit(1); | 587 | exit(1); |
579 | } | 588 | } |
580 | EUID_ROOT(); // grsecurity fix | 589 | EUID_ROOT(); // grsecurity fix |
581 | FILE *fp = fopen(file, "r"); | 590 | FILE *fp = fopen(file, "r"); |
582 | if (!fp) { | 591 | if (!fp) { |
583 | free(file); | 592 | free(file); |
@@ -596,16 +605,16 @@ uid_t pid_get_uid(pid_t pid) { | |||
596 | } | 605 | } |
597 | if (*ptr == '\0') | 606 | if (*ptr == '\0') |
598 | break; | 607 | break; |
599 | 608 | ||
600 | rv = atoi(ptr); | 609 | rv = atoi(ptr); |
601 | break; // break regardless! | 610 | break; // break regardless! |
602 | } | 611 | } |
603 | } | 612 | } |
604 | 613 | ||
605 | fclose(fp); | 614 | fclose(fp); |
606 | free(file); | 615 | free(file); |
607 | EUID_USER(); // grsecurity fix | 616 | EUID_USER(); // grsecurity fix |
608 | 617 | ||
609 | if (rv == 0) { | 618 | if (rv == 0) { |
610 | fprintf(stderr, "Error: cannot read /proc file\n"); | 619 | fprintf(stderr, "Error: cannot read /proc file\n"); |
611 | exit(1); | 620 | exit(1); |
@@ -613,14 +622,15 @@ uid_t pid_get_uid(pid_t pid) { | |||
613 | return rv; | 622 | return rv; |
614 | } | 623 | } |
615 | 624 | ||
625 | |||
616 | void invalid_filename(const char *fname) { | 626 | void invalid_filename(const char *fname) { |
617 | EUID_ASSERT(); | 627 | EUID_ASSERT(); |
618 | assert(fname); | 628 | assert(fname); |
619 | const char *ptr = fname; | 629 | const char *ptr = fname; |
620 | 630 | ||
621 | if (arg_debug_check_filename) | 631 | if (arg_debug_check_filename) |
622 | printf("Checking filename %s\n", fname); | 632 | printf("Checking filename %s\n", fname); |
623 | 633 | ||
624 | if (strncmp(ptr, "${HOME}", 7) == 0) | 634 | if (strncmp(ptr, "${HOME}", 7) == 0) |
625 | ptr = fname + 7; | 635 | ptr = fname + 7; |
626 | else if (strncmp(ptr, "${PATH}", 7) == 0) | 636 | else if (strncmp(ptr, "${PATH}", 7) == 0) |
@@ -636,22 +646,125 @@ void invalid_filename(const char *fname) { | |||
636 | } | 646 | } |
637 | } | 647 | } |
638 | 648 | ||
639 | uid_t get_tty_gid(void) { | 649 | |
650 | uid_t get_group_id(const char *group) { | ||
640 | // find tty group id | 651 | // find tty group id |
641 | gid_t ttygid = 0; | 652 | gid_t gid = 0; |
642 | struct group *g = getgrnam("tty"); | 653 | struct group *g = getgrnam(group); |
643 | if (g) | 654 | if (g) |
644 | ttygid = g->gr_gid; | 655 | gid = g->gr_gid; |
645 | 656 | ||
646 | return ttygid; | 657 | return gid; |
647 | } | 658 | } |
648 | 659 | ||
649 | uid_t get_audio_gid(void) { | ||
650 | // find tty group id | ||
651 | gid_t audiogid = 0; | ||
652 | struct group *g = getgrnam("audio"); | ||
653 | if (g) | ||
654 | audiogid = g->gr_gid; | ||
655 | 660 | ||
656 | return audiogid; | 661 | static int remove_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { |
662 | (void) sb; | ||
663 | (void) typeflag; | ||
664 | (void) ftwbuf; | ||
665 | |||
666 | int rv = remove(fpath); | ||
667 | if (rv) | ||
668 | perror(fpath); | ||
669 | |||
670 | return rv; | ||
671 | } | ||
672 | |||
673 | |||
674 | int remove_directory(const char *path) { | ||
675 | // FTW_PHYS - do not follow symbolic links | ||
676 | return nftw(path, remove_callback, 64, FTW_DEPTH | FTW_PHYS); | ||
677 | } | ||
678 | |||
679 | void flush_stdin(void) { | ||
680 | if (isatty(STDIN_FILENO)) { | ||
681 | int cnt = 0; | ||
682 | ioctl(STDIN_FILENO, FIONREAD, &cnt); | ||
683 | if (cnt) { | ||
684 | if (!arg_quiet) | ||
685 | printf("Warning: removing %d bytes from stdin\n", cnt); | ||
686 | ioctl(STDIN_FILENO, TCFLSH, TCIFLUSH); | ||
687 | } | ||
688 | } | ||
689 | } | ||
690 | |||
691 | void create_empty_dir_as_root(const char *dir, mode_t mode) { | ||
692 | assert(dir); | ||
693 | struct stat s; | ||
694 | |||
695 | if (stat(dir, &s)) { | ||
696 | if (arg_debug) | ||
697 | printf("Creating empty %s directory\n", dir); | ||
698 | if (mkdir(dir, mode) == -1) | ||
699 | errExit("mkdir"); | ||
700 | if (set_perms(dir, 0, 0, mode)) | ||
701 | errExit("set_perms"); | ||
702 | ASSERT_PERMS(dir, 0, 0, mode); | ||
703 | } | ||
704 | } | ||
705 | |||
706 | void create_empty_file_as_root(const char *fname, mode_t mode) { | ||
707 | assert(fname); | ||
708 | struct stat s; | ||
709 | |||
710 | if (stat(fname, &s)) { | ||
711 | if (arg_debug) | ||
712 | printf("Creating empty %s file\n", fname); | ||
713 | |||
714 | FILE *fp = fopen(fname, "w"); | ||
715 | if (!fp) | ||
716 | errExit("fopen"); | ||
717 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR); | ||
718 | fclose(fp); | ||
719 | if (chmod(fname, mode) == -1) | ||
720 | errExit("chmod"); | ||
721 | } | ||
722 | } | ||
723 | |||
724 | // return 1 if error | ||
725 | int set_perms(const char *fname, uid_t uid, gid_t gid, mode_t mode) { | ||
726 | assert(fname); | ||
727 | if (chmod(fname, mode) == -1) | ||
728 | return 1; | ||
729 | if (chown(fname, uid, gid) == -1) | ||
730 | return 1; | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid) { | ||
735 | assert(fname); | ||
736 | mode &= 07777; | ||
737 | #if 0 | ||
738 | printf("fname %s, uid %d, gid %d, mode %x - ", fname, uid, gid, (unsigned) mode); | ||
739 | if (S_ISLNK(mode)) | ||
740 | printf("l"); | ||
741 | else if (S_ISDIR(mode)) | ||
742 | printf("d"); | ||
743 | else if (S_ISCHR(mode)) | ||
744 | printf("c"); | ||
745 | else if (S_ISBLK(mode)) | ||
746 | printf("b"); | ||
747 | else if (S_ISSOCK(mode)) | ||
748 | printf("s"); | ||
749 | else | ||
750 | printf("-"); | ||
751 | printf( (mode & S_IRUSR) ? "r" : "-"); | ||
752 | printf( (mode & S_IWUSR) ? "w" : "-"); | ||
753 | printf( (mode & S_IXUSR) ? "x" : "-"); | ||
754 | printf( (mode & S_IRGRP) ? "r" : "-"); | ||
755 | printf( (mode & S_IWGRP) ? "w" : "-"); | ||
756 | printf( (mode & S_IXGRP) ? "x" : "-"); | ||
757 | printf( (mode & S_IROTH) ? "r" : "-"); | ||
758 | printf( (mode & S_IWOTH) ? "w" : "-"); | ||
759 | printf( (mode & S_IXOTH) ? "x" : "-"); | ||
760 | printf("\n"); | ||
761 | #endif | ||
762 | if (mkdir(fname, mode) == -1 || | ||
763 | chmod(fname, mode) == -1 || | ||
764 | chown(fname, uid, gid)) { | ||
765 | fprintf(stderr, "Error: failed to create %s directory\n", fname); | ||
766 | errExit("mkdir/chmod"); | ||
767 | } | ||
768 | |||
769 | ASSERT_PERMS(fname, uid, gid, mode); | ||
657 | } | 770 | } |
diff --git a/src/firejail/x11.c b/src/firejail/x11.c index ef1095a49..9da6d3e30 100644 --- a/src/firejail/x11.c +++ b/src/firejail/x11.c | |||
@@ -20,11 +20,14 @@ | |||
20 | #include "firejail.h" | 20 | #include "firejail.h" |
21 | #include <sys/types.h> | 21 | #include <sys/types.h> |
22 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
23 | #include <fcntl.h> | ||
23 | #include <unistd.h> | 24 | #include <unistd.h> |
24 | #include <signal.h> | 25 | #include <signal.h> |
25 | #include <stdlib.h> | 26 | #include <stdlib.h> |
26 | #include <dirent.h> | 27 | #include <dirent.h> |
27 | #include <sys/mount.h> | 28 | #include <sys/mount.h> |
29 | #include <sys/wait.h> | ||
30 | int mask_x11_abstract_socket = 0; | ||
28 | 31 | ||
29 | #ifdef HAVE_X11 | 32 | #ifdef HAVE_X11 |
30 | // return 1 if xpra is installed on the system | 33 | // return 1 if xpra is installed on the system |
@@ -49,6 +52,31 @@ static int x11_check_xephyr(void) { | |||
49 | return 1; | 52 | return 1; |
50 | } | 53 | } |
51 | 54 | ||
55 | // check for X11 abstract sockets | ||
56 | static int x11_abstract_sockets_present(void) { | ||
57 | char *path; | ||
58 | |||
59 | EUID_ROOT(); // grsecurity fix | ||
60 | FILE *fp = fopen("/proc/net/unix", "r"); | ||
61 | EUID_USER(); | ||
62 | |||
63 | if (!fp) | ||
64 | errExit("fopen"); | ||
65 | |||
66 | while (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %ms\n", &path) != EOF) { | ||
67 | if (path && strncmp(path, "@/tmp/.X11-unix/", 16) == 0) { | ||
68 | free(path); | ||
69 | fclose(fp); | ||
70 | return 1; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | free(path); | ||
75 | fclose(fp); | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
52 | static int random_display_number(void) { | 80 | static int random_display_number(void) { |
53 | int i; | 81 | int i; |
54 | int found = 1; | 82 | int found = 1; |
@@ -109,10 +137,8 @@ void fs_x11(void) { | |||
109 | int rv = mkdir(RUN_WHITELIST_X11_DIR, 1777); | 137 | int rv = mkdir(RUN_WHITELIST_X11_DIR, 1777); |
110 | if (rv == -1) | 138 | if (rv == -1) |
111 | errExit("mkdir"); | 139 | errExit("mkdir"); |
112 | if (chown(RUN_WHITELIST_X11_DIR, 0, 0) < 0) | 140 | if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 1777)) |
113 | errExit("chown"); | 141 | errExit("set_perms"); |
114 | if (chmod(RUN_WHITELIST_X11_DIR, 1777) < 0) | ||
115 | errExit("chmod"); | ||
116 | 142 | ||
117 | if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 143 | if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
118 | errExit("mount bind"); | 144 | errExit("mount bind"); |
@@ -130,13 +156,9 @@ void fs_x11(void) { | |||
130 | fprintf(stderr, "Error: cannot create empty file in x11 directory\n"); | 156 | fprintf(stderr, "Error: cannot create empty file in x11 directory\n"); |
131 | exit(1); | 157 | exit(1); |
132 | } | 158 | } |
133 | fclose(fp); | ||
134 | |||
135 | // set file properties | 159 | // set file properties |
136 | if (chown(x11file, s.st_uid, s.st_gid) < 0) | 160 | SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); |
137 | errExit("chown"); | 161 | fclose(fp); |
138 | if (chmod(x11file, s.st_mode) < 0) | ||
139 | errExit("chmod"); | ||
140 | 162 | ||
141 | // mount | 163 | // mount |
142 | char *wx11file; | 164 | char *wx11file; |
@@ -164,15 +186,17 @@ void x11_start_xephyr(int argc, char **argv) { | |||
164 | EUID_ASSERT(); | 186 | EUID_ASSERT(); |
165 | int i; | 187 | int i; |
166 | struct stat s; | 188 | struct stat s; |
167 | pid_t client = 0; | 189 | pid_t jail = 0; |
168 | pid_t server = 0; | 190 | pid_t server = 0; |
169 | 191 | ||
192 | setenv("FIREJAIL_X11", "yes", 1); | ||
170 | 193 | ||
171 | // unfortunately, xephyr does a number of weird things when started by root user!!! | 194 | // unfortunately, xephyr does a number of weird things when started by root user!!! |
172 | if (getuid() == 0) { | 195 | if (getuid() == 0) { |
173 | fprintf(stderr, "Error: this feature is not available when running as root\n"); | 196 | fprintf(stderr, "Error: X11 sandboxing is not available when running as root\n"); |
174 | exit(1); | 197 | exit(1); |
175 | } | 198 | } |
199 | drop_privs(0); | ||
176 | 200 | ||
177 | // check xephyr | 201 | // check xephyr |
178 | if (x11_check_xephyr() == 0) { | 202 | if (x11_check_xephyr() == 0) { |
@@ -183,23 +207,78 @@ void x11_start_xephyr(int argc, char **argv) { | |||
183 | } | 207 | } |
184 | 208 | ||
185 | int display = random_display_number(); | 209 | int display = random_display_number(); |
186 | 210 | char *display_str; | |
187 | // start xephyr | 211 | if (asprintf(&display_str, ":%d", display) == -1) |
188 | char *cmd1; | ||
189 | if (asprintf(&cmd1, "Xephyr -ac -br -title \"firejail x11 sandbox\" -noreset -screen %s :%d", xephyr_screen, display) == -1) | ||
190 | errExit("asprintf"); | 212 | errExit("asprintf"); |
191 | 213 | ||
192 | int len = 50; // DISPLAY... | 214 | assert(xephyr_screen); |
193 | for (i = 0; i < argc; i++) { | 215 | char *server_argv[256] = { "Xephyr", "-ac", "-br", "-noreset", "-screen", xephyr_screen }; // rest initialyzed to NULL |
194 | len += strlen(argv[i]) + 1; // + ' ' | 216 | unsigned pos = 0; |
217 | while (server_argv[pos] != NULL) pos++; | ||
218 | if (checkcfg(CFG_XEPHYR_WINDOW_TITLE)) { | ||
219 | server_argv[pos++] = "-title"; | ||
220 | server_argv[pos++] = "firejail x11 sandbox"; | ||
221 | } | ||
222 | |||
223 | assert(xephyr_extra_params); // should be "" if empty | ||
224 | |||
225 | // parse xephyr_extra_params | ||
226 | // very basic quoting support | ||
227 | char *temp = strdup(xephyr_extra_params); | ||
228 | if (*xephyr_extra_params != '\0') { | ||
229 | if (!temp) | ||
230 | errExit("strdup"); | ||
231 | bool dquote = false; | ||
232 | bool squote = false; | ||
233 | for (i = 0; i < (int) strlen(xephyr_extra_params); i++) { | ||
234 | if (temp[i] == '\"') { | ||
235 | dquote = !dquote; | ||
236 | if (dquote) temp[i] = '\0'; // replace closing quote by \0 | ||
237 | } | ||
238 | if (temp[i] == '\'') { | ||
239 | squote = !squote; | ||
240 | if (squote) temp[i] = '\0'; // replace closing quote by \0 | ||
241 | } | ||
242 | if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0'; | ||
243 | if (dquote && squote) { | ||
244 | fprintf(stderr, "Error: mixed quoting found while parsing xephyr_extra_params\n"); | ||
245 | exit(1); | ||
246 | } | ||
247 | } | ||
248 | if (dquote) { | ||
249 | fprintf(stderr, "Error: unclosed quote found while parsing xephyr_extra_params\n"); | ||
250 | exit(1); | ||
251 | } | ||
252 | |||
253 | for (i = 0; i < (int) strlen(xephyr_extra_params)-1; i++) { | ||
254 | if (pos >= (sizeof(server_argv)/sizeof(*server_argv))) { | ||
255 | fprintf(stderr, "Error: arg count limit exceeded while parsing xephyr_extra_params\n"); | ||
256 | exit(1); | ||
257 | } | ||
258 | if (temp[i] == '\0' && (temp[i+1] == '\"' || temp[i+1] == '\'')) server_argv[pos++] = temp + i + 2; | ||
259 | else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1; | ||
260 | } | ||
195 | } | 261 | } |
196 | 262 | ||
197 | char *cmd2 = malloc(len + 1); // + '\0' | 263 | server_argv[pos++] = display_str; |
198 | if (!cmd2) | 264 | server_argv[pos++] = NULL; |
199 | errExit("malloc"); | 265 | |
266 | assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); // no overrun | ||
267 | assert(server_argv[pos-1] == NULL); // last element is null | ||
200 | 268 | ||
201 | sprintf(cmd2, "DISPLAY=:%d ", display); | 269 | if (arg_debug) { |
202 | char *ptr = cmd2 + strlen(cmd2); | 270 | size_t i = 0; |
271 | printf("xephyr server:"); | ||
272 | while (server_argv[i]!=NULL) { | ||
273 | printf(" \"%s\"", server_argv[i]); | ||
274 | i++; | ||
275 | } | ||
276 | putchar('\n'); | ||
277 | } | ||
278 | |||
279 | // remove --x11 arg | ||
280 | char *jail_argv[argc+2]; | ||
281 | int j = 0; | ||
203 | for (i = 0; i < argc; i++) { | 282 | for (i = 0; i < argc; i++) { |
204 | if (strcmp(argv[i], "--x11") == 0) | 283 | if (strcmp(argv[i], "--x11") == 0) |
205 | continue; | 284 | continue; |
@@ -207,32 +286,40 @@ void x11_start_xephyr(int argc, char **argv) { | |||
207 | continue; | 286 | continue; |
208 | if (strcmp(argv[i], "--x11=xephyr") == 0) | 287 | if (strcmp(argv[i], "--x11=xephyr") == 0) |
209 | continue; | 288 | continue; |
210 | ptr += sprintf(ptr, "%s ", argv[i]); | 289 | jail_argv[j] = argv[i]; |
290 | j++; | ||
291 | } | ||
292 | jail_argv[j] = NULL; | ||
293 | |||
294 | assert(j < argc+2); // no overrun | ||
295 | |||
296 | if (arg_debug) { | ||
297 | size_t i = 0; | ||
298 | printf("xephyr client:"); | ||
299 | while (jail_argv[i]!=NULL) { | ||
300 | printf(" \"%s\"", jail_argv[i]); | ||
301 | i++; | ||
302 | } | ||
303 | putchar('\n'); | ||
211 | } | 304 | } |
212 | if (arg_debug) | ||
213 | printf("xephyr server: %s\n", cmd1); | ||
214 | if (arg_debug) | ||
215 | printf("xephyr client: %s\n", cmd2); | ||
216 | 305 | ||
217 | signal(SIGHUP,SIG_IGN); // fix sleep(1) below | ||
218 | server = fork(); | 306 | server = fork(); |
219 | if (server < 0) | 307 | if (server < 0) |
220 | errExit("fork"); | 308 | errExit("fork"); |
221 | if (server == 0) { | 309 | if (server == 0) { |
222 | if (arg_debug) | 310 | if (arg_debug) |
223 | printf("Starting xephyr...\n"); | 311 | printf("Starting xephyr...\n"); |
224 | 312 | ||
225 | char *a[4]; | 313 | // running without privileges - see drop_privs call above |
226 | a[0] = "/bin/bash"; | 314 | assert(getenv("LD_PRELOAD") == NULL); |
227 | a[1] = "-c"; | 315 | execvp(server_argv[0], server_argv); |
228 | a[2] = cmd1; | ||
229 | a[3] = NULL; | ||
230 | |||
231 | execvp(a[0], a); | ||
232 | perror("execvp"); | 316 | perror("execvp"); |
233 | exit(1); | 317 | _exit(1); |
234 | } | 318 | } |
235 | 319 | ||
320 | if (arg_debug) | ||
321 | printf("xephyr server pid %d\n", server); | ||
322 | |||
236 | // check X11 socket | 323 | // check X11 socket |
237 | char *fname; | 324 | char *fname; |
238 | if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1) | 325 | if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1) |
@@ -250,7 +337,6 @@ void x11_start_xephyr(int argc, char **argv) { | |||
250 | exit(1); | 337 | exit(1); |
251 | } | 338 | } |
252 | free(fname); | 339 | free(fname); |
253 | sleep(1); | ||
254 | 340 | ||
255 | if (arg_debug) { | 341 | if (arg_debug) { |
256 | printf("X11 sockets: "); fflush(0); | 342 | printf("X11 sockets: "); fflush(0); |
@@ -258,26 +344,40 @@ void x11_start_xephyr(int argc, char **argv) { | |||
258 | (void) rv; | 344 | (void) rv; |
259 | } | 345 | } |
260 | 346 | ||
347 | setenv("DISPLAY", display_str, 1); | ||
261 | // run attach command | 348 | // run attach command |
262 | client = fork(); | 349 | jail = fork(); |
263 | if (client < 0) | 350 | if (jail < 0) |
264 | errExit("fork"); | 351 | errExit("fork"); |
265 | if (client == 0) { | 352 | if (jail == 0) { |
266 | printf("\n*** Attaching to Xephyr display %d ***\n\n", display); | 353 | if (!arg_quiet) |
267 | char *a[4]; | 354 | printf("\n*** Attaching to Xephyr display %d ***\n\n", display); |
268 | a[0] = "/bin/bash"; | 355 | |
269 | a[1] = "-c"; | 356 | // running without privileges - see drop_privs call above |
270 | a[2] = cmd2; | 357 | assert(getenv("LD_PRELOAD") == NULL); |
271 | a[3] = NULL; | 358 | execvp(jail_argv[0], jail_argv); |
272 | |||
273 | execvp(a[0], a); | ||
274 | perror("execvp"); | 359 | perror("execvp"); |
275 | exit(1); | 360 | _exit(1); |
276 | } | 361 | } |
277 | sleep(1); | 362 | |
278 | 363 | // cleanup | |
279 | if (!arg_quiet) | 364 | free(display_str); |
280 | printf("Xephyr server pid %d, client pid %d\n", server, client); | 365 | free(temp); |
366 | |||
367 | // wait for either server or jail termination | ||
368 | pid_t pid = wait(NULL); | ||
369 | |||
370 | // see which process terminated and kill other | ||
371 | if (pid == server) { | ||
372 | kill(jail, SIGTERM); | ||
373 | } else if (pid == jail) { | ||
374 | kill(server, SIGTERM); | ||
375 | } | ||
376 | |||
377 | // without this closing Xephyr window may mess your terminal: | ||
378 | // "monitoring" process will release terminal before | ||
379 | // jail process ends and releases terminal | ||
380 | wait(NULL); // fulneral | ||
281 | 381 | ||
282 | exit(0); | 382 | exit(0); |
283 | } | 383 | } |
@@ -289,12 +389,14 @@ void x11_start_xpra(int argc, char **argv) { | |||
289 | pid_t client = 0; | 389 | pid_t client = 0; |
290 | pid_t server = 0; | 390 | pid_t server = 0; |
291 | 391 | ||
392 | setenv("FIREJAIL_X11", "yes", 1); | ||
292 | 393 | ||
293 | // unfortunately, xpra does a number of weird things when started by root user!!! | 394 | // unfortunately, xpra does a number of weird things when started by root user!!! |
294 | if (getuid() == 0) { | 395 | if (getuid() == 0) { |
295 | fprintf(stderr, "Error: this feature is not available when running as root\n"); | 396 | fprintf(stderr, "Error: X11 sandboxing is not available when running as root\n"); |
296 | exit(1); | 397 | exit(1); |
297 | } | 398 | } |
399 | drop_privs(0); | ||
298 | 400 | ||
299 | // check xpra | 401 | // check xpra |
300 | if (x11_check_xpra() == 0) { | 402 | if (x11_check_xpra() == 0) { |
@@ -304,56 +406,39 @@ void x11_start_xpra(int argc, char **argv) { | |||
304 | } | 406 | } |
305 | 407 | ||
306 | int display = random_display_number(); | 408 | int display = random_display_number(); |
409 | char *display_str; | ||
410 | if (asprintf(&display_str, ":%d", display) == -1) | ||
411 | errExit("asprintf"); | ||
307 | 412 | ||
308 | // build the start command | 413 | // build the start command |
309 | int len = 50; // xpra start... | 414 | char *server_argv[] = { "xpra", "start", display_str, "--no-daemon", NULL }; |
310 | for (i = 0; i < argc; i++) { | 415 | |
311 | len += strlen(argv[i]) + 1; // + ' ' | 416 | int fd_null = -1; |
312 | } | 417 | if (arg_quiet) { |
313 | 418 | fd_null = open("/dev/null", O_RDWR); | |
314 | char *cmd1 = malloc(len + 1); // + '\0' | 419 | if (fd_null == -1) |
315 | if (!cmd1) | 420 | errExit("open"); |
316 | errExit("malloc"); | ||
317 | |||
318 | sprintf(cmd1, "xpra start :%d --exit-with-children --start-child=\"", display); | ||
319 | char *ptr = cmd1 + strlen(cmd1); | ||
320 | for (i = 0; i < argc; i++) { | ||
321 | if (strcmp(argv[i], "--x11") == 0) | ||
322 | continue; | ||
323 | if (strcmp(argv[i], "--x11=xpra") == 0) | ||
324 | continue; | ||
325 | if (strcmp(argv[i], "--x11=xephyr") == 0) | ||
326 | continue; | ||
327 | ptr += sprintf(ptr, "%s ", argv[i]); | ||
328 | } | 421 | } |
329 | sprintf(ptr, "\""); | ||
330 | if (arg_debug) | ||
331 | printf("xpra server: %s\n", cmd1); | ||
332 | |||
333 | // build the attach command | ||
334 | char *cmd2; | ||
335 | if (asprintf(&cmd2, "xpra --title=\"firejail x11 sandbox\" attach :%d", display) == -1) | ||
336 | errExit("asprintf"); | ||
337 | if (arg_debug) | ||
338 | printf("xpra client: %s\n", cmd2); | ||
339 | 422 | ||
340 | signal(SIGHUP,SIG_IGN); // fix sleep(1) below | 423 | // start |
341 | server = fork(); | 424 | server = fork(); |
342 | if (server < 0) | 425 | if (server < 0) |
343 | errExit("fork"); | 426 | errExit("fork"); |
344 | if (server == 0) { | 427 | if (server == 0) { |
345 | if (arg_debug) | 428 | if (arg_debug) |
346 | printf("Starting xpra...\n"); | 429 | printf("Starting xpra...\n"); |
430 | |||
431 | if (arg_quiet && fd_null != -1) { | ||
432 | dup2(fd_null,0); | ||
433 | dup2(fd_null,1); | ||
434 | dup2(fd_null,2); | ||
435 | } | ||
347 | 436 | ||
348 | char *a[4]; | 437 | // running without privileges - see drop_privs call above |
349 | a[0] = "/bin/bash"; | 438 | assert(getenv("LD_PRELOAD") == NULL); |
350 | a[1] = "-c"; | 439 | execvp(server_argv[0], server_argv); |
351 | a[2] = cmd1; | ||
352 | a[3] = NULL; | ||
353 | |||
354 | execvp(a[0], a); | ||
355 | perror("execvp"); | 440 | perror("execvp"); |
356 | exit(1); | 441 | _exit(1); |
357 | } | 442 | } |
358 | 443 | ||
359 | // check X11 socket | 444 | // check X11 socket |
@@ -366,14 +451,13 @@ void x11_start_xpra(int argc, char **argv) { | |||
366 | sleep(1); | 451 | sleep(1); |
367 | if (stat(fname, &s) == 0) | 452 | if (stat(fname, &s) == 0) |
368 | break; | 453 | break; |
369 | }; | 454 | } |
370 | 455 | ||
371 | if (n == 10) { | 456 | if (n == 10) { |
372 | fprintf(stderr, "Error: failed to start xpra\n"); | 457 | fprintf(stderr, "Error: failed to start xpra\n"); |
373 | exit(1); | 458 | exit(1); |
374 | } | 459 | } |
375 | free(fname); | 460 | free(fname); |
376 | sleep(1); | ||
377 | 461 | ||
378 | if (arg_debug) { | 462 | if (arg_debug) { |
379 | printf("X11 sockets: "); fflush(0); | 463 | printf("X11 sockets: "); fflush(0); |
@@ -381,28 +465,118 @@ void x11_start_xpra(int argc, char **argv) { | |||
381 | (void) rv; | 465 | (void) rv; |
382 | } | 466 | } |
383 | 467 | ||
468 | // build attach command | ||
469 | char *attach_argv[] = { "xpra", "--title=\"firejail x11 sandbox\"", "attach", display_str, NULL }; | ||
470 | |||
384 | // run attach command | 471 | // run attach command |
385 | client = fork(); | 472 | client = fork(); |
386 | if (client < 0) | 473 | if (client < 0) |
387 | errExit("fork"); | 474 | errExit("fork"); |
388 | if (client == 0) { | 475 | if (client == 0) { |
389 | printf("\n*** Attaching to xpra display %d ***\n\n", display); | 476 | if (arg_quiet && fd_null != -1) { |
390 | char *a[4]; | 477 | dup2(fd_null,0); |
391 | a[0] = "/bin/bash"; | 478 | dup2(fd_null,1); |
392 | a[1] = "-c"; | 479 | dup2(fd_null,2); |
393 | a[2] = cmd2; | 480 | } |
394 | a[3] = NULL; | 481 | |
395 | 482 | if (!arg_quiet) | |
396 | execvp(a[0], a); | 483 | printf("\n*** Attaching to xpra display %d ***\n\n", display); |
484 | |||
485 | // running without privileges - see drop_privs call above | ||
486 | assert(getenv("LD_PRELOAD") == NULL); | ||
487 | execvp(attach_argv[0], attach_argv); | ||
488 | perror("execvp"); | ||
489 | _exit(1); | ||
490 | } | ||
491 | |||
492 | setenv("DISPLAY", display_str, 1); | ||
493 | |||
494 | // build jail command | ||
495 | char *firejail_argv[argc+2]; | ||
496 | int pos = 0; | ||
497 | for (i = 0; i < argc; i++) { | ||
498 | if (strcmp(argv[i], "--x11") == 0) | ||
499 | continue; | ||
500 | if (strcmp(argv[i], "--x11=xpra") == 0) | ||
501 | continue; | ||
502 | if (strcmp(argv[i], "--x11=xephyr") == 0) | ||
503 | continue; | ||
504 | firejail_argv[pos] = argv[i]; | ||
505 | pos++; | ||
506 | } | ||
507 | firejail_argv[pos] = NULL; | ||
508 | |||
509 | assert(pos < (argc+2)); | ||
510 | assert(!firejail_argv[pos]); | ||
511 | |||
512 | // start jail | ||
513 | pid_t jail = fork(); | ||
514 | if (jail < 0) | ||
515 | errExit("fork"); | ||
516 | if (jail == 0) { | ||
517 | // running without privileges - see drop_privs call above | ||
518 | assert(getenv("LD_PRELOAD") == NULL); | ||
519 | if (firejail_argv[0]) // shut up llvm scan-build | ||
520 | execvp(firejail_argv[0], firejail_argv); | ||
397 | perror("execvp"); | 521 | perror("execvp"); |
398 | exit(1); | 522 | exit(1); |
399 | } | 523 | } |
400 | sleep(1); | ||
401 | |||
402 | if (!arg_quiet) | ||
403 | printf("Xpra server pid %d, client pid %d\n", server, client); | ||
404 | 524 | ||
405 | exit(0); | 525 | if (!arg_quiet) |
526 | printf("Xpra server pid %d, xpra client pid %d, jail %d\n", server, client, jail); | ||
527 | |||
528 | sleep(1); // let jail start | ||
529 | |||
530 | // wait for jail or server to end | ||
531 | while (1) { | ||
532 | pid_t pid = wait(NULL); | ||
533 | |||
534 | if (pid == jail) { | ||
535 | char *stop_argv[] = { "xpra", "stop", display_str, NULL }; | ||
536 | pid_t stop = fork(); | ||
537 | if (stop < 0) | ||
538 | errExit("fork"); | ||
539 | if (stop == 0) { | ||
540 | if (arg_quiet && fd_null != -1) { | ||
541 | dup2(fd_null,0); | ||
542 | dup2(fd_null,1); | ||
543 | dup2(fd_null,2); | ||
544 | } | ||
545 | // running without privileges - see drop_privs call above | ||
546 | assert(getenv("LD_PRELOAD") == NULL); | ||
547 | execvp(stop_argv[0], stop_argv); | ||
548 | perror("execvp"); | ||
549 | _exit(1); | ||
550 | } | ||
551 | |||
552 | // wait for xpra server to stop, 10 seconds limit | ||
553 | while (++n < 10) { | ||
554 | sleep(1); | ||
555 | pid = waitpid(server, NULL, WNOHANG); | ||
556 | if (pid == server) | ||
557 | break; | ||
558 | } | ||
559 | |||
560 | if (arg_debug) { | ||
561 | if (n == 10) | ||
562 | printf("failed to stop xpra server gratefully\n"); | ||
563 | else | ||
564 | printf("xpra server successfully stopped in %d secs\n", n); | ||
565 | } | ||
566 | |||
567 | // kill xpra server and xpra client | ||
568 | kill(client, SIGTERM); | ||
569 | kill(server, SIGTERM); | ||
570 | exit(0); | ||
571 | } | ||
572 | else if (pid == server) { | ||
573 | // kill firejail process | ||
574 | kill(jail, SIGTERM); | ||
575 | // kill xpra client (should die with server, but...) | ||
576 | kill(client, SIGTERM); | ||
577 | exit(0); | ||
578 | } | ||
579 | } | ||
406 | } | 580 | } |
407 | 581 | ||
408 | void x11_start(int argc, char **argv) { | 582 | void x11_start(int argc, char **argv) { |
@@ -410,7 +584,7 @@ void x11_start(int argc, char **argv) { | |||
410 | 584 | ||
411 | // unfortunately, xpra does a number of weird things when started by root user!!! | 585 | // unfortunately, xpra does a number of weird things when started by root user!!! |
412 | if (getuid() == 0) { | 586 | if (getuid() == 0) { |
413 | fprintf(stderr, "Error: this feature is not available when running as root\n"); | 587 | fprintf(stderr, "Error: X11 sandboxing is not available when running as root\n"); |
414 | exit(1); | 588 | exit(1); |
415 | } | 589 | } |
416 | 590 | ||
@@ -428,3 +602,128 @@ void x11_start(int argc, char **argv) { | |||
428 | } | 602 | } |
429 | 603 | ||
430 | #endif | 604 | #endif |
605 | |||
606 | void x11_block(void) { | ||
607 | #ifdef HAVE_X11 | ||
608 | mask_x11_abstract_socket = 1; | ||
609 | |||
610 | // check abstract socket presence and network namespace options | ||
611 | if ((!arg_nonetwork && !cfg.bridge0.configured && !cfg.interface0.configured) | ||
612 | && x11_abstract_sockets_present()) { | ||
613 | fprintf(stderr, "ERROR: --x11=none specified, but abstract X11 socket still accessible.\n" | ||
614 | "Additional setup required. To block abstract X11 socket you can either:\n" | ||
615 | " * use network namespace in firejail (--net=none, --net=...)\n" | ||
616 | " * add \"-nolisten local\" to xserver options\n" | ||
617 | " (eg. to your display manager config, or /etc/X11/xinit/xserverrc)\n"); | ||
618 | exit(1); | ||
619 | } | ||
620 | |||
621 | // blacklist sockets | ||
622 | profile_check_line("blacklist /tmp/.X11-unix", 0, NULL); | ||
623 | profile_add(strdup("blacklist /tmp/.X11-unix")); | ||
624 | |||
625 | // blacklist .Xauthority | ||
626 | profile_check_line("blacklist ${HOME}/.Xauthority", 0, NULL); | ||
627 | profile_add(strdup("blacklist ${HOME}/.Xauthority")); | ||
628 | char *xauthority = getenv("XAUTHORITY"); | ||
629 | if (xauthority) { | ||
630 | char *line; | ||
631 | if (asprintf(&line, "blacklist %s", xauthority) == -1) | ||
632 | errExit("asprintf"); | ||
633 | profile_check_line(line, 0, NULL); | ||
634 | profile_add(line); | ||
635 | } | ||
636 | |||
637 | // clear environment | ||
638 | env_store("DISPLAY", RMENV); | ||
639 | env_store("XAUTHORITY", RMENV); | ||
640 | #endif | ||
641 | } | ||
642 | |||
643 | void x11_xorg(void) { | ||
644 | #ifdef HAVE_X11 | ||
645 | // destination - create an empty ~/.Xauthotrity file if it doesn't exist already, and use it as a mount point | ||
646 | char *dest; | ||
647 | if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) | ||
648 | errExit("asprintf"); | ||
649 | struct stat s; | ||
650 | if (stat(dest, &s) == -1) { | ||
651 | // create an .Xauthority file | ||
652 | FILE *fp = fopen(dest, "w"); | ||
653 | if (!fp) | ||
654 | errExit("fopen"); | ||
655 | SET_PERMS_STREAM(fp, getuid(), getgid(), 0600); | ||
656 | fclose(fp); | ||
657 | } | ||
658 | |||
659 | // check xauth utility is present in the system | ||
660 | if (stat("/usr/bin/xauth", &s) == -1) { | ||
661 | fprintf(stderr, "Error: cannot find /usr/bin/xauth executable\n"); | ||
662 | exit(1); | ||
663 | } | ||
664 | |||
665 | // create a temporary .Xauthority file | ||
666 | char tmpfname[] = "/tmp/.tmpXauth-XXXXXX"; | ||
667 | int fd = mkstemp(tmpfname); | ||
668 | if (fd == -1) { | ||
669 | fprintf(stderr, "Error: cannot create .Xauthority file\n"); | ||
670 | exit(1); | ||
671 | } | ||
672 | close(fd); | ||
673 | if (chown(tmpfname, getuid(), getgid()) == -1) | ||
674 | errExit("chown"); | ||
675 | |||
676 | pid_t child = fork(); | ||
677 | if (child < 0) | ||
678 | errExit("fork"); | ||
679 | if (child == 0) { | ||
680 | // generate the new .Xauthority file using xauth utility | ||
681 | if (arg_debug) | ||
682 | printf("Generating a new .Xauthority file\n"); | ||
683 | drop_privs(1); | ||
684 | |||
685 | char *display = getenv("DISPLAY"); | ||
686 | if (!display) | ||
687 | display = ":0.0"; | ||
688 | |||
689 | clearenv(); | ||
690 | execlp("/usr/bin/xauth", "/usr/bin/xauth", "-f", tmpfname, | ||
691 | "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL); | ||
692 | |||
693 | #ifdef HAVE_GCOV | ||
694 | __gcov_flush(); | ||
695 | #endif | ||
696 | _exit(0); | ||
697 | } | ||
698 | |||
699 | // wait for the child to finish | ||
700 | waitpid(child, NULL, 0); | ||
701 | |||
702 | // check the file was created and set mode and ownership | ||
703 | if (stat(tmpfname, &s) == -1) { | ||
704 | fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); | ||
705 | exit(1); | ||
706 | } | ||
707 | if (set_perms(tmpfname, getuid(), getgid(), 0600)) | ||
708 | errExit("set_perms"); | ||
709 | |||
710 | // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted | ||
711 | // automatically when the sandbox is closed | ||
712 | if (copy_file(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) { | ||
713 | fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); | ||
714 | exit(1); | ||
715 | } | ||
716 | if (set_perms(RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) | ||
717 | errExit("set_perms"); | ||
718 | unlink(tmpfname); | ||
719 | |||
720 | // mount | ||
721 | if (mount(RUN_XAUTHORITY_SEC_FILE, dest, "none", MS_BIND, "mode=0600") == -1) { | ||
722 | fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); | ||
723 | exit(1); | ||
724 | } | ||
725 | if (set_perms(dest, getuid(), getgid(), 0600)) | ||
726 | errExit("set_perms"); | ||
727 | free(dest); | ||
728 | #endif | ||
729 | } | ||
diff --git a/src/firemon/Makefile.in b/src/firemon/Makefile.in index 21888d354..efc48b212 100644 --- a/src/firemon/Makefile.in +++ b/src/firemon/Makefile.in | |||
@@ -4,21 +4,26 @@ PREFIX=@prefix@ | |||
4 | VERSION=@PACKAGE_VERSION@ | 4 | VERSION=@PACKAGE_VERSION@ |
5 | NAME=@PACKAGE_NAME@ | 5 | NAME=@PACKAGE_NAME@ |
6 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ | 6 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ |
7 | HAVE_GCOV=@HAVE_GCOV@ | ||
8 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
7 | 9 | ||
8 | H_FILE_LIST = $(sort $(wildcard *.[h])) | 10 | H_FILE_LIST = $(sort $(wildcard *.[h])) |
9 | C_FILE_LIST = $(sort $(wildcard *.c)) | 11 | C_FILE_LIST = $(sort $(wildcard *.c)) |
10 | OBJS = $(C_FILE_LIST:.c=.o) | 12 | OBJS = $(C_FILE_LIST:.c=.o) |
11 | BINOBJS = $(foreach file, $(OBJS), $file) | 13 | BINOBJS = $(foreach file, $(OBJS), $file) |
12 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | 14 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security |
13 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now | 15 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now |
16 | HAVE_GCOV=@HAVE_GCOV@ | ||
17 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
18 | |||
14 | 19 | ||
15 | %.o : %.c $(H_FILE_LIST) | 20 | %.o : %.c $(H_FILE_LIST) |
16 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | 21 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ |
17 | 22 | ||
18 | firemon: $(OBJS) ../lib/common.o ../lib/pid.o | 23 | firemon: $(OBJS) ../lib/common.o ../lib/pid.o |
19 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/pid.o $(LIBS) | 24 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/pid.o $(LIBS) $(EXTRA_LDFLAGS) |
20 | 25 | ||
21 | clean:; rm -f *.o firemon | 26 | clean:; rm -f *.o firemon *.gcov *.gcda *.gcno |
22 | 27 | ||
23 | distclean: clean | 28 | distclean: clean |
24 | rm -fr Makefile | 29 | rm -fr Makefile |
diff --git a/src/firemon/arp.c b/src/firemon/arp.c index 7cb8ff4c3..014f6a904 100644 --- a/src/firemon/arp.c +++ b/src/firemon/arp.c | |||
@@ -72,17 +72,15 @@ static void print_arp(const char *fname) { | |||
72 | 72 | ||
73 | } | 73 | } |
74 | 74 | ||
75 | void arp(pid_t pid) { | 75 | void arp(pid_t pid, int print_procs) { |
76 | if (getuid() == 0) | ||
77 | firemon_drop_privs(); | ||
78 | |||
79 | pid_read(pid); | 76 | pid_read(pid); |
80 | 77 | ||
81 | // print processes | 78 | // print processes |
82 | int i; | 79 | int i; |
83 | for (i = 0; i < max_pids; i++) { | 80 | for (i = 0; i < max_pids; i++) { |
84 | if (pids[i].level == 1) { | 81 | if (pids[i].level == 1) { |
85 | pid_print_list(i, 0); | 82 | if (print_procs || pid == 0) |
83 | pid_print_list(i, 0); | ||
86 | int child = find_child(i); | 84 | int child = find_child(i); |
87 | if (child != -1) { | 85 | if (child != -1) { |
88 | char *fname; | 86 | char *fname; |
@@ -90,10 +88,10 @@ void arp(pid_t pid) { | |||
90 | errExit("asprintf"); | 88 | errExit("asprintf"); |
91 | print_arp(fname); | 89 | print_arp(fname); |
92 | free(fname); | 90 | free(fname); |
93 | printf("\n"); | ||
94 | } | 91 | } |
95 | } | 92 | } |
96 | } | 93 | } |
94 | printf("\n"); | ||
97 | } | 95 | } |
98 | 96 | ||
99 | 97 | ||
diff --git a/src/firemon/caps.c b/src/firemon/caps.c index 5cd9b5d0d..81877ab87 100644 --- a/src/firemon/caps.c +++ b/src/firemon/caps.c | |||
@@ -48,17 +48,15 @@ static void print_caps(int pid) { | |||
48 | free(file); | 48 | free(file); |
49 | } | 49 | } |
50 | 50 | ||
51 | void caps(pid_t pid) { | 51 | void caps(pid_t pid, int print_procs) { |
52 | if (getuid() == 0) | ||
53 | firemon_drop_privs(); | ||
54 | |||
55 | pid_read(pid); // include all processes | 52 | pid_read(pid); // include all processes |
56 | 53 | ||
57 | // print processes | 54 | // print processes |
58 | int i; | 55 | int i; |
59 | for (i = 0; i < max_pids; i++) { | 56 | for (i = 0; i < max_pids; i++) { |
60 | if (pids[i].level == 1) { | 57 | if (pids[i].level == 1) { |
61 | pid_print_list(i, 0); | 58 | if (print_procs || pid == 0) |
59 | pid_print_list(i, 0); | ||
62 | int child = find_child(i); | 60 | int child = find_child(i); |
63 | if (child != -1) | 61 | if (child != -1) |
64 | print_caps(child); | 62 | print_caps(child); |
diff --git a/src/firemon/cgroup.c b/src/firemon/cgroup.c index 0b93390ae..e20e1d449 100644 --- a/src/firemon/cgroup.c +++ b/src/firemon/cgroup.c | |||
@@ -44,21 +44,20 @@ static void print_cgroup(int pid) { | |||
44 | free(file); | 44 | free(file); |
45 | } | 45 | } |
46 | 46 | ||
47 | void cgroup(pid_t pid) { | 47 | void cgroup(pid_t pid, int print_procs) { |
48 | if (getuid() == 0) | ||
49 | firemon_drop_privs(); | ||
50 | |||
51 | pid_read(pid); | 48 | pid_read(pid); |
52 | 49 | ||
53 | // print processes | 50 | // print processes |
54 | int i; | 51 | int i; |
55 | for (i = 0; i < max_pids; i++) { | 52 | for (i = 0; i < max_pids; i++) { |
56 | if (pids[i].level == 1) { | 53 | if (pids[i].level == 1) { |
57 | pid_print_list(i, 0); | 54 | if (print_procs || pid == 0) |
55 | pid_print_list(i, 0); | ||
58 | int child = find_child(i); | 56 | int child = find_child(i); |
59 | if (child != -1) | 57 | if (child != -1) |
60 | print_cgroup(child); | 58 | print_cgroup(child); |
61 | } | 59 | } |
62 | } | 60 | } |
61 | printf("\n"); | ||
63 | } | 62 | } |
64 | 63 | ||
diff --git a/src/firemon/cpu.c b/src/firemon/cpu.c index 06658f58c..47c935686 100644 --- a/src/firemon/cpu.c +++ b/src/firemon/cpu.c | |||
@@ -48,21 +48,20 @@ static void print_cpu(int pid) { | |||
48 | free(file); | 48 | free(file); |
49 | } | 49 | } |
50 | 50 | ||
51 | void cpu(pid_t pid) { | 51 | void cpu(pid_t pid, int print_procs) { |
52 | if (getuid() == 0) | ||
53 | firemon_drop_privs(); | ||
54 | |||
55 | pid_read(pid); | 52 | pid_read(pid); |
56 | 53 | ||
57 | // print processes | 54 | // print processes |
58 | int i; | 55 | int i; |
59 | for (i = 0; i < max_pids; i++) { | 56 | for (i = 0; i < max_pids; i++) { |
60 | if (pids[i].level == 1) { | 57 | if (pids[i].level == 1) { |
61 | pid_print_list(i, 0); | 58 | if (print_procs || pid == 0) |
59 | pid_print_list(i, 0); | ||
62 | int child = find_child(i); | 60 | int child = find_child(i); |
63 | if (child != -1) | 61 | if (child != -1) |
64 | print_cpu(child); | 62 | print_cpu(child); |
65 | } | 63 | } |
66 | } | 64 | } |
65 | printf("\n"); | ||
67 | } | 66 | } |
68 | 67 | ||
diff --git a/src/firemon/firemon.c b/src/firemon/firemon.c index 3140c5f70..b63e37444 100644 --- a/src/firemon/firemon.c +++ b/src/firemon/firemon.c | |||
@@ -25,7 +25,6 @@ | |||
25 | #include <grp.h> | 25 | #include <grp.h> |
26 | #include <sys/stat.h> | 26 | #include <sys/stat.h> |
27 | 27 | ||
28 | |||
29 | static int arg_route = 0; | 28 | static int arg_route = 0; |
30 | static int arg_arp = 0; | 29 | static int arg_arp = 0; |
31 | static int arg_tree = 0; | 30 | static int arg_tree = 0; |
@@ -35,6 +34,9 @@ static int arg_caps = 0; | |||
35 | static int arg_cpu = 0; | 34 | static int arg_cpu = 0; |
36 | static int arg_cgroup = 0; | 35 | static int arg_cgroup = 0; |
37 | static int arg_x11 = 0; | 36 | static int arg_x11 = 0; |
37 | static int arg_top = 0; | ||
38 | static int arg_list = 0; | ||
39 | static int arg_netstats = 0; | ||
38 | int arg_nowrap = 0; | 40 | int arg_nowrap = 0; |
39 | 41 | ||
40 | static struct termios tlocal; // startup terminal setting | 42 | static struct termios tlocal; // startup terminal setting |
@@ -62,17 +64,6 @@ int find_child(int id) { | |||
62 | return -1; | 64 | return -1; |
63 | } | 65 | } |
64 | 66 | ||
65 | // drop privileges | ||
66 | void firemon_drop_privs(void) { | ||
67 | // drop privileges | ||
68 | if (setgroups(0, NULL) < 0) | ||
69 | errExit("setgroups"); | ||
70 | if (setgid(getgid()) < 0) | ||
71 | errExit("setgid/getgid"); | ||
72 | if (setuid(getuid()) < 0) | ||
73 | errExit("setuid/getuid"); | ||
74 | } | ||
75 | |||
76 | // sleep and wait for a key to be pressed | 67 | // sleep and wait for a key to be pressed |
77 | void firemon_sleep(int st) { | 68 | void firemon_sleep(int st) { |
78 | if (terminal_set == 0) { | 69 | if (terminal_set == 0) { |
@@ -129,53 +120,44 @@ int main(int argc, char **argv) { | |||
129 | } | 120 | } |
130 | 121 | ||
131 | // options without a pid argument | 122 | // options without a pid argument |
132 | else if (strcmp(argv[i], "--top") == 0) { | 123 | else if (strcmp(argv[i], "--top") == 0) |
133 | top(); // never to return | 124 | arg_top = 1; |
134 | } | 125 | else if (strcmp(argv[i], "--list") == 0) |
135 | else if (strcmp(argv[i], "--list") == 0) { | 126 | arg_list = 1; |
136 | list(); | 127 | else if (strcmp(argv[i], "--tree") == 0) |
137 | return 0; | 128 | arg_tree = 1; |
138 | } | ||
139 | else if (strcmp(argv[i], "--netstats") == 0) { | 129 | else if (strcmp(argv[i], "--netstats") == 0) { |
140 | struct stat s; | 130 | struct stat s; |
141 | if (getuid() != 0 && stat("/proc/sys/kernel/grsecurity", &s) == 0) { | 131 | if (getuid() != 0 && stat("/proc/sys/kernel/grsecurity", &s) == 0) { |
142 | fprintf(stderr, "Error: this feature is not available on Grsecurity systems\n"); | 132 | fprintf(stderr, "Error: this feature is not available on Grsecurity systems\n"); |
143 | exit(1); | 133 | exit(1); |
144 | } | 134 | } |
145 | 135 | arg_netstats = 1; | |
146 | netstats(); | ||
147 | return 0; | ||
148 | } | 136 | } |
149 | 137 | ||
150 | 138 | ||
151 | // cumulative options with or without a pid argument | 139 | // cumulative options with or without a pid argument |
152 | else if (strcmp(argv[i], "--x11") == 0) { | 140 | else if (strcmp(argv[i], "--x11") == 0) |
153 | arg_x11 = 1; | 141 | arg_x11 = 1; |
154 | } | 142 | else if (strcmp(argv[i], "--cgroup") == 0) |
155 | else if (strcmp(argv[i], "--cgroup") == 0) { | ||
156 | arg_cgroup = 1; | 143 | arg_cgroup = 1; |
157 | } | 144 | else if (strcmp(argv[i], "--cpu") == 0) |
158 | else if (strcmp(argv[i], "--cpu") == 0) { | ||
159 | arg_cpu = 1; | 145 | arg_cpu = 1; |
160 | } | 146 | else if (strcmp(argv[i], "--seccomp") == 0) |
161 | else if (strcmp(argv[i], "--seccomp") == 0) { | ||
162 | arg_seccomp = 1; | 147 | arg_seccomp = 1; |
163 | } | 148 | else if (strcmp(argv[i], "--caps") == 0) |
164 | else if (strcmp(argv[i], "--caps") == 0) { | ||
165 | arg_caps = 1; | 149 | arg_caps = 1; |
166 | } | ||
167 | else if (strcmp(argv[i], "--tree") == 0) { | ||
168 | arg_tree = 1; | ||
169 | } | ||
170 | else if (strcmp(argv[i], "--interface") == 0) { | 150 | else if (strcmp(argv[i], "--interface") == 0) { |
151 | if (getuid() != 0) { | ||
152 | fprintf(stderr, "Error: you need to be root to run this command\n"); | ||
153 | exit(1); | ||
154 | } | ||
171 | arg_interface = 1; | 155 | arg_interface = 1; |
172 | } | 156 | } |
173 | else if (strcmp(argv[i], "--route") == 0) { | 157 | else if (strcmp(argv[i], "--route") == 0) |
174 | arg_route = 1; | 158 | arg_route = 1; |
175 | } | 159 | else if (strcmp(argv[i], "--arp") == 0) |
176 | else if (strcmp(argv[i], "--arp") == 0) { | ||
177 | arg_arp = 1; | 160 | arg_arp = 1; |
178 | } | ||
179 | 161 | ||
180 | else if (strncmp(argv[i], "--name=", 7) == 0) { | 162 | else if (strncmp(argv[i], "--name=", 7) == 0) { |
181 | char *name = argv[i] + 7; | 163 | char *name = argv[i] + 7; |
@@ -212,27 +194,66 @@ int main(int argc, char **argv) { | |||
212 | } | 194 | } |
213 | } | 195 | } |
214 | 196 | ||
215 | if (arg_tree) | 197 | // allow only root user if /proc is mounted hidepid |
198 | if (pid_hidepid() && getuid() != 0) { | ||
199 | fprintf(stderr, "Error: /proc is mounted hidepid, you would need to be root to run this command\n"); | ||
200 | exit(1); | ||
201 | } | ||
202 | |||
203 | if (arg_top) { | ||
204 | top(); | ||
205 | return 0; | ||
206 | } | ||
207 | if (arg_list) { | ||
208 | list(); | ||
209 | return 0; | ||
210 | } | ||
211 | if (arg_netstats) { | ||
212 | netstats(); | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | // cumulative options | ||
217 | int print_procs = 1; | ||
218 | if (arg_tree) { | ||
216 | tree((pid_t) pid); | 219 | tree((pid_t) pid); |
217 | if (arg_interface) | 220 | print_procs = 0; |
218 | interface((pid_t) pid); | 221 | } |
219 | if (arg_route) | 222 | if (arg_cpu) { |
220 | route((pid_t) pid); | 223 | cpu((pid_t) pid, print_procs); |
221 | if (arg_arp) | 224 | print_procs = 0; |
222 | arp((pid_t) pid); | 225 | } |
223 | if (arg_seccomp) | 226 | if (arg_seccomp) { |
224 | seccomp((pid_t) pid); | 227 | seccomp((pid_t) pid, print_procs); |
225 | if (arg_caps) | 228 | print_procs = 0; |
226 | caps((pid_t) pid); | 229 | } |
227 | if (arg_cpu) | 230 | if (arg_caps) { |
228 | cpu((pid_t) pid); | 231 | caps((pid_t) pid, print_procs); |
229 | if (arg_cgroup) | 232 | print_procs = 0; |
230 | cgroup((pid_t) pid); | 233 | } |
231 | if (arg_x11) | 234 | if (arg_cgroup) { |
232 | x11((pid_t) pid); | 235 | cgroup((pid_t) pid, print_procs); |
236 | print_procs = 0; | ||
237 | } | ||
238 | if (arg_x11) { | ||
239 | x11((pid_t) pid, print_procs); | ||
240 | print_procs = 0; | ||
241 | } | ||
242 | if (arg_interface) { | ||
243 | interface((pid_t) pid, print_procs); | ||
244 | print_procs = 0; | ||
245 | } | ||
246 | if (arg_route) { | ||
247 | route((pid_t) pid, print_procs); | ||
248 | print_procs = 0; | ||
249 | } | ||
250 | if (arg_arp) { | ||
251 | arp((pid_t) pid, print_procs); | ||
252 | print_procs = 0; | ||
253 | } | ||
233 | 254 | ||
234 | if (!arg_route && !arg_arp && !arg_interface && !arg_tree && !arg_caps && !arg_seccomp && !arg_x11) | 255 | if (print_procs) |
235 | procevent((pid_t) pid); // never to return | 256 | procevent((pid_t) pid); |
236 | 257 | ||
237 | return 0; | 258 | return 0; |
238 | } | 259 | } |
diff --git a/src/firemon/firemon.h b/src/firemon/firemon.h index 522ece077..c78023888 100644 --- a/src/firemon/firemon.h +++ b/src/firemon/firemon.h | |||
@@ -38,7 +38,6 @@ static inline void firemon_clrscr(void) { | |||
38 | // firemon.c | 38 | // firemon.c |
39 | extern int arg_nowrap; | 39 | extern int arg_nowrap; |
40 | int find_child(int id); | 40 | int find_child(int id); |
41 | void firemon_drop_privs(void); | ||
42 | void firemon_sleep(int st); | 41 | void firemon_sleep(int st); |
43 | 42 | ||
44 | 43 | ||
@@ -55,25 +54,25 @@ void top(void); | |||
55 | void list(void); | 54 | void list(void); |
56 | 55 | ||
57 | // interface.c | 56 | // interface.c |
58 | void interface(pid_t pid); | 57 | void interface(pid_t pid, int print_procs); |
59 | 58 | ||
60 | // arp.c | 59 | // arp.c |
61 | void arp(pid_t pid); | 60 | void arp(pid_t pid, int print_procs); |
62 | 61 | ||
63 | // route.c | 62 | // route.c |
64 | void route(pid_t pid); | 63 | void route(pid_t pid, int print_procs); |
65 | 64 | ||
66 | // caps.c | 65 | // caps.c |
67 | void caps(pid_t pid); | 66 | void caps(pid_t pid, int print_procs); |
68 | 67 | ||
69 | // seccomp.c | 68 | // seccomp.c |
70 | void seccomp(pid_t pid); | 69 | void seccomp(pid_t pid, int print_procs); |
71 | 70 | ||
72 | // cpu.c | 71 | // cpu.c |
73 | void cpu(pid_t pid); | 72 | void cpu(pid_t pid, int print_procs); |
74 | 73 | ||
75 | // cgroup.c | 74 | // cgroup.c |
76 | void cgroup(pid_t pid); | 75 | void cgroup(pid_t pid, int print_procs); |
77 | 76 | ||
78 | // tree.c | 77 | // tree.c |
79 | void tree(pid_t pid); | 78 | void tree(pid_t pid); |
@@ -82,6 +81,6 @@ void tree(pid_t pid); | |||
82 | void netstats(void); | 81 | void netstats(void); |
83 | 82 | ||
84 | // x11.c | 83 | // x11.c |
85 | void x11(pid_t pid); | 84 | void x11(pid_t pid, int print_procs); |
86 | 85 | ||
87 | #endif | 86 | #endif |
diff --git a/src/firemon/interface.c b/src/firemon/interface.c index 5a89e1491..def9cd5ac 100644 --- a/src/firemon/interface.c +++ b/src/firemon/interface.c | |||
@@ -145,32 +145,31 @@ static void print_sandbox(pid_t pid) { | |||
145 | if (rv) | 145 | if (rv) |
146 | return; | 146 | return; |
147 | net_ifprint(); | 147 | net_ifprint(); |
148 | printf("\n"); | 148 | #ifdef HAVE_GCOV |
149 | exit(0); | 149 | __gcov_flush(); |
150 | #endif | ||
151 | _exit(0); | ||
150 | } | 152 | } |
151 | 153 | ||
152 | // wait for the child to finish | 154 | // wait for the child to finish |
153 | waitpid(child, NULL, 0); | 155 | waitpid(child, NULL, 0); |
154 | } | 156 | } |
155 | 157 | ||
156 | void interface(pid_t pid) { | 158 | void interface(pid_t pid, int print_procs) { |
157 | if (getuid() != 0) { | ||
158 | fprintf(stderr, "Error: you need to be root to run this command\n"); | ||
159 | exit(1); | ||
160 | } | ||
161 | |||
162 | pid_read(pid); // a pid of 0 will include all processes | 159 | pid_read(pid); // a pid of 0 will include all processes |
163 | 160 | ||
164 | // print processes | 161 | // print processes |
165 | int i; | 162 | int i; |
166 | for (i = 0; i < max_pids; i++) { | 163 | for (i = 0; i < max_pids; i++) { |
167 | if (pids[i].level == 1) { | 164 | if (pids[i].level == 1) { |
168 | pid_print_list(i, 0); | 165 | if (print_procs || pid == 0) |
166 | pid_print_list(i, 0); | ||
169 | int child = find_child(i); | 167 | int child = find_child(i); |
170 | if (child != -1) { | 168 | if (child != -1) { |
171 | print_sandbox(child); | 169 | print_sandbox(child); |
172 | } | 170 | } |
173 | } | 171 | } |
174 | } | 172 | } |
173 | printf("\n"); | ||
175 | } | 174 | } |
176 | 175 | ||
diff --git a/src/firemon/list.c b/src/firemon/list.c index 901627c2a..acff13a28 100644 --- a/src/firemon/list.c +++ b/src/firemon/list.c | |||
@@ -20,9 +20,6 @@ | |||
20 | #include "firemon.h" | 20 | #include "firemon.h" |
21 | 21 | ||
22 | void list(void) { | 22 | void list(void) { |
23 | if (getuid() == 0) | ||
24 | firemon_drop_privs(); | ||
25 | |||
26 | pid_read(0); // include all processes | 23 | pid_read(0); // include all processes |
27 | 24 | ||
28 | // print processes | 25 | // print processes |
diff --git a/src/firemon/netstats.c b/src/firemon/netstats.c index 89e4202bd..3c020d630 100644 --- a/src/firemon/netstats.c +++ b/src/firemon/netstats.c | |||
@@ -26,6 +26,10 @@ | |||
26 | 26 | ||
27 | #define MAXBUF 4096 | 27 | #define MAXBUF 4096 |
28 | 28 | ||
29 | // ip -s link: device stats | ||
30 | // ss -s: socket stats | ||
31 | |||
32 | |||
29 | static char *get_header(void) { | 33 | static char *get_header(void) { |
30 | char *rv; | 34 | char *rv; |
31 | if (asprintf(&rv, "%-5.5s %-9.9s %-10.10s %-10.10s %s", | 35 | if (asprintf(&rv, "%-5.5s %-9.9s %-10.10s %-10.10s %s", |
@@ -166,9 +170,6 @@ static void print_proc(int index, int itv, int col) { | |||
166 | } | 170 | } |
167 | 171 | ||
168 | void netstats(void) { | 172 | void netstats(void) { |
169 | if (getuid() == 0) | ||
170 | firemon_drop_privs(); | ||
171 | |||
172 | pid_read(0); // include all processes | 173 | pid_read(0); // include all processes |
173 | 174 | ||
174 | printf("Displaying network statistics only for sandboxes using a new network namespace.\n"); | 175 | printf("Displaying network statistics only for sandboxes using a new network namespace.\n"); |
diff --git a/src/firemon/procevent.c b/src/firemon/procevent.c index e2dd5aaa2..1940f4a34 100644 --- a/src/firemon/procevent.c +++ b/src/firemon/procevent.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <arpa/inet.h> | 28 | #include <arpa/inet.h> |
29 | #include <time.h> | 29 | #include <time.h> |
30 | #include <fcntl.h> | 30 | #include <fcntl.h> |
31 | #include <sys/uio.h> | ||
32 | |||
31 | #define PIDS_BUFLEN 4096 | 33 | #define PIDS_BUFLEN 4096 |
32 | #define SERVER_PORT 889 // 889-899 is left unassigned by IANA | 34 | #define SERVER_PORT 889 // 889-899 is left unassigned by IANA |
33 | 35 | ||
@@ -89,7 +91,8 @@ static int pid_is_firejail(pid_t pid) { | |||
89 | 91 | ||
90 | // list of firejail arguments that don't trigger sandbox creation | 92 | // list of firejail arguments that don't trigger sandbox creation |
91 | // the initial -- is not included | 93 | // the initial -- is not included |
92 | char *firejail_args = "ls list tree x11 help version top netstats debug-syscalls debug-errnos debug-protocols"; | 94 | char *firejail_args = "ls list tree x11 help version top netstats debug-syscalls debug-errnos debug-protocols " |
95 | "protocol.print debug.caps shutdown bandwidth caps.print cpu.print debug-caps fs.print get overlay-clean "; | ||
93 | 96 | ||
94 | int i; | 97 | int i; |
95 | char *start; | 98 | char *start; |
@@ -189,6 +192,10 @@ static int procevent_monitor(const int sock, pid_t mypid) { | |||
189 | tv.tv_usec = 0; | 192 | tv.tv_usec = 0; |
190 | 193 | ||
191 | while (1) { | 194 | while (1) { |
195 | #ifdef HAVE_GCOV | ||
196 | __gcov_flush(); | ||
197 | #endif | ||
198 | |||
192 | #define BUFFSIZE 4096 | 199 | #define BUFFSIZE 4096 |
193 | char __attribute__ ((aligned(NLMSG_ALIGNTO)))buf[BUFFSIZE]; | 200 | char __attribute__ ((aligned(NLMSG_ALIGNTO)))buf[BUFFSIZE]; |
194 | 201 | ||
diff --git a/src/firemon/route.c b/src/firemon/route.c index 398965671..fb58b169d 100644 --- a/src/firemon/route.c +++ b/src/firemon/route.c | |||
@@ -181,17 +181,15 @@ static void print_route(const char *fname) { | |||
181 | 181 | ||
182 | } | 182 | } |
183 | 183 | ||
184 | void route(pid_t pid) { | 184 | void route(pid_t pid, int print_procs) { |
185 | if (getuid() == 0) | ||
186 | firemon_drop_privs(); | ||
187 | |||
188 | pid_read(pid); | 185 | pid_read(pid); |
189 | 186 | ||
190 | // print processes | 187 | // print processes |
191 | int i; | 188 | int i; |
192 | for (i = 0; i < max_pids; i++) { | 189 | for (i = 0; i < max_pids; i++) { |
193 | if (pids[i].level == 1) { | 190 | if (pids[i].level == 1) { |
194 | pid_print_list(i, 0); | 191 | if (print_procs || pid == 0) |
192 | pid_print_list(i, 0); | ||
195 | int child = find_child(i); | 193 | int child = find_child(i); |
196 | if (child != -1) { | 194 | if (child != -1) { |
197 | char *fname; | 195 | char *fname; |
@@ -204,10 +202,10 @@ void route(pid_t pid) { | |||
204 | errExit("asprintf"); | 202 | errExit("asprintf"); |
205 | print_route(fname); | 203 | print_route(fname); |
206 | free(fname); | 204 | free(fname); |
207 | printf("\n"); | ||
208 | } | 205 | } |
209 | } | 206 | } |
210 | } | 207 | } |
208 | printf("\n"); | ||
211 | } | 209 | } |
212 | 210 | ||
213 | 211 | ||
diff --git a/src/firemon/seccomp.c b/src/firemon/seccomp.c index 71771c72d..abc698bb8 100644 --- a/src/firemon/seccomp.c +++ b/src/firemon/seccomp.c | |||
@@ -48,17 +48,15 @@ static void print_seccomp(int pid) { | |||
48 | free(file); | 48 | free(file); |
49 | } | 49 | } |
50 | 50 | ||
51 | void seccomp(pid_t pid) { | 51 | void seccomp(pid_t pid, int print_procs) { |
52 | if (getuid() == 0) | ||
53 | firemon_drop_privs(); | ||
54 | |||
55 | pid_read(pid); // include all processes | 52 | pid_read(pid); // include all processes |
56 | 53 | ||
57 | // print processes | 54 | // print processes |
58 | int i; | 55 | int i; |
59 | for (i = 0; i < max_pids; i++) { | 56 | for (i = 0; i < max_pids; i++) { |
60 | if (pids[i].level == 1) { | 57 | if (pids[i].level == 1) { |
61 | pid_print_list(i, 0); | 58 | if (print_procs || pid == 0) |
59 | pid_print_list(i, 0); | ||
62 | int child = find_child(i); | 60 | int child = find_child(i); |
63 | if (child != -1) | 61 | if (child != -1) |
64 | print_seccomp(child); | 62 | print_seccomp(child); |
diff --git a/src/firemon/top.c b/src/firemon/top.c index a6da6f64e..b804761dd 100644 --- a/src/firemon/top.c +++ b/src/firemon/top.c | |||
@@ -232,9 +232,6 @@ void head_print(int col, int row) { | |||
232 | } | 232 | } |
233 | 233 | ||
234 | void top(void) { | 234 | void top(void) { |
235 | if (getuid() == 0) | ||
236 | firemon_drop_privs(); | ||
237 | |||
238 | while (1) { | 235 | while (1) { |
239 | // clear linked list | 236 | // clear linked list |
240 | head_clear(); | 237 | head_clear(); |
diff --git a/src/firemon/tree.c b/src/firemon/tree.c index b05eb92f9..6d8b37ecb 100644 --- a/src/firemon/tree.c +++ b/src/firemon/tree.c | |||
@@ -20,10 +20,7 @@ | |||
20 | #include "firemon.h" | 20 | #include "firemon.h" |
21 | 21 | ||
22 | void tree(pid_t pid) { | 22 | void tree(pid_t pid) { |
23 | if (getuid() == 0) | 23 | pid_read(pid); |
24 | firemon_drop_privs(); | ||
25 | |||
26 | pid_read(pid); // include all processes | ||
27 | 24 | ||
28 | // print processes | 25 | // print processes |
29 | int i; | 26 | int i; |
diff --git a/src/firemon/x11.c b/src/firemon/x11.c index e30c2d78b..b0efb090a 100644 --- a/src/firemon/x11.c +++ b/src/firemon/x11.c | |||
@@ -22,17 +22,15 @@ | |||
22 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
23 | #include <unistd.h> | 23 | #include <unistd.h> |
24 | 24 | ||
25 | void x11(pid_t pid) { | 25 | void x11(pid_t pid, int print_procs) { |
26 | if (getuid() == 0) | ||
27 | firemon_drop_privs(); | ||
28 | |||
29 | pid_read(pid); | 26 | pid_read(pid); |
30 | 27 | ||
31 | // print processes | 28 | // print processes |
32 | int i; | 29 | int i; |
33 | for (i = 0; i < max_pids; i++) { | 30 | for (i = 0; i < max_pids; i++) { |
34 | if (pids[i].level == 1) { | 31 | if (pids[i].level == 1) { |
35 | pid_print_list(i, 0); | 32 | if (print_procs || pid == 0) |
33 | pid_print_list(i, 0); | ||
36 | 34 | ||
37 | char *x11file; | 35 | char *x11file; |
38 | // todo: use macro from src/firejail/firejail.h for /run/firejail/x11 directory | 36 | // todo: use macro from src/firejail/firejail.h for /run/firejail/x11 directory |
@@ -49,12 +47,13 @@ void x11(pid_t pid) { | |||
49 | int display; | 47 | int display; |
50 | int rv = fscanf(fp, "%d", &display); | 48 | int rv = fscanf(fp, "%d", &display); |
51 | if (rv == 1) | 49 | if (rv == 1) |
52 | printf(" DISPLAY :%d\n", display); | 50 | printf(" DISPLAY :%d\n", display); |
53 | fclose(fp); | 51 | fclose(fp); |
54 | } | 52 | } |
55 | 53 | ||
56 | free(x11file); | 54 | free(x11file); |
57 | } | 55 | } |
58 | } | 56 | } |
57 | printf("\n"); | ||
59 | } | 58 | } |
60 | 59 | ||
diff --git a/src/fnet/Makefile.in b/src/fnet/Makefile.in new file mode 100644 index 000000000..32f08882a --- /dev/null +++ b/src/fnet/Makefile.in | |||
@@ -0,0 +1,45 @@ | |||
1 | all: fnet | ||
2 | |||
3 | prefix=@prefix@ | ||
4 | exec_prefix=@exec_prefix@ | ||
5 | libdir=@libdir@ | ||
6 | sysconfdir=@sysconfdir@ | ||
7 | |||
8 | VERSION=@PACKAGE_VERSION@ | ||
9 | NAME=@PACKAGE_NAME@ | ||
10 | HAVE_SECCOMP_H=@HAVE_SECCOMP_H@ | ||
11 | HAVE_SECCOMP=@HAVE_SECCOMP@ | ||
12 | HAVE_CHROOT=@HAVE_CHROOT@ | ||
13 | HAVE_BIND=@HAVE_BIND@ | ||
14 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ | ||
15 | HAVE_NETWORK=@HAVE_NETWORK@ | ||
16 | HAVE_USERNS=@HAVE_USERNS@ | ||
17 | HAVE_X11=@HAVE_X11@ | ||
18 | HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ | ||
19 | HAVE_WHITELIST=@HAVE_WHITELIST@ | ||
20 | HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ | ||
21 | HAVE_APPARMOR=@HAVE_APPARMOR@ | ||
22 | HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ | ||
23 | HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@ | ||
24 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
25 | HAVE_GCOV=@HAVE_GCOV@ | ||
26 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
27 | |||
28 | H_FILE_LIST = $(sort $(wildcard *.[h])) | ||
29 | C_FILE_LIST = $(sort $(wildcard *.c)) | ||
30 | OBJS = $(C_FILE_LIST:.c=.o) | ||
31 | BINOBJS = $(foreach file, $(OBJS), $file) | ||
32 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | ||
33 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | ||
34 | |||
35 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/libnetlink.h | ||
36 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | ||
37 | |||
38 | fnet: $(OBJS) ../lib/libnetlink.o | ||
39 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o $(LIBS) $(EXTRA_LDFLAGS) | ||
40 | |||
41 | clean:; rm -f *.o fnet *.gcov *.gcda *.gcno | ||
42 | |||
43 | distclean: clean | ||
44 | rm -fr Makefile | ||
45 | |||
diff --git a/src/fnet/arp.c b/src/fnet/arp.c new file mode 100644 index 000000000..96684fdf9 --- /dev/null +++ b/src/fnet/arp.c | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fnet.h" | ||
21 | #include <sys/socket.h> | ||
22 | #include <sys/ioctl.h> | ||
23 | #include <linux/if_ether.h> //TCP/IP Protocol Suite for Linux | ||
24 | #include <net/if.h> | ||
25 | #include <netinet/in.h> | ||
26 | #include <linux/ip.h> | ||
27 | #include <linux/udp.h> | ||
28 | #include <linux/tcp.h> | ||
29 | #include <linux/if_packet.h> | ||
30 | |||
31 | typedef struct arp_hdr_t { | ||
32 | uint16_t htype; | ||
33 | uint16_t ptype; | ||
34 | uint8_t hlen; | ||
35 | uint8_t plen; | ||
36 | uint16_t opcode; | ||
37 | uint8_t sender_mac[6]; | ||
38 | uint8_t sender_ip[4]; | ||
39 | uint8_t target_mac[6]; | ||
40 | uint8_t target_ip[4]; | ||
41 | } ArpHdr; | ||
42 | |||
43 | |||
44 | // scan interface (--scan option) | ||
45 | void arp_scan(const char *dev, uint32_t ifip, uint32_t ifmask) { | ||
46 | assert(dev); | ||
47 | assert(ifip); | ||
48 | |||
49 | // printf("Scanning interface %s (%d.%d.%d.%d/%d)\n", | ||
50 | // dev, PRINT_IP(ifip & ifmask), mask2bits(ifmask)); | ||
51 | |||
52 | if (strlen(dev) > IFNAMSIZ) { | ||
53 | fprintf(stderr, "Error: invalid network device name %s\n", dev); | ||
54 | exit(1); | ||
55 | } | ||
56 | |||
57 | // find interface mac address | ||
58 | int sock; | ||
59 | if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) | ||
60 | errExit("socket"); | ||
61 | struct ifreq ifr; | ||
62 | memset(&ifr, 0, sizeof (ifr)); | ||
63 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); | ||
64 | if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) | ||
65 | errExit("ioctl"); | ||
66 | close(sock); | ||
67 | uint8_t mac[6]; | ||
68 | memcpy (mac, ifr.ifr_hwaddr.sa_data, 6); | ||
69 | |||
70 | // open layer2 socket | ||
71 | if ((sock = socket(PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) | ||
72 | errExit("socket"); | ||
73 | |||
74 | // try all possible ip addresses in ascending order | ||
75 | uint32_t range = ~ifmask + 1; // the number of potential addresses | ||
76 | // this software is not supported for /31 networks | ||
77 | if (range < 4) { | ||
78 | fprintf(stderr, "Warning: this option is not supported for /31 networks\n"); | ||
79 | close(sock); | ||
80 | return; | ||
81 | } | ||
82 | |||
83 | uint32_t dest = (ifip & ifmask) + 1; | ||
84 | uint32_t last = dest + range - 1; | ||
85 | uint32_t src = htonl(ifip); | ||
86 | |||
87 | // wait not more than one second for an answer | ||
88 | int header_printed = 0; | ||
89 | uint32_t last_ip = 0; | ||
90 | struct timeval ts; | ||
91 | ts.tv_sec = 2; // 2 seconds receive timeout | ||
92 | ts.tv_usec = 0; | ||
93 | |||
94 | while (1) { | ||
95 | fd_set rfds; | ||
96 | FD_ZERO(&rfds); | ||
97 | FD_SET(sock, &rfds); | ||
98 | fd_set wfds; | ||
99 | FD_ZERO(&wfds); | ||
100 | FD_SET(sock, &wfds); | ||
101 | int maxfd = sock; | ||
102 | |||
103 | uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc | ||
104 | memset(frame, 0, ETH_FRAME_LEN); | ||
105 | |||
106 | int nready; | ||
107 | if (dest < last) | ||
108 | nready = select(maxfd + 1, &rfds, &wfds, (fd_set *) 0, NULL); | ||
109 | else | ||
110 | nready = select(maxfd + 1, &rfds, (fd_set *) 0, (fd_set *) 0, &ts); | ||
111 | |||
112 | if (nready < 0) | ||
113 | errExit("select"); | ||
114 | |||
115 | if (nready == 0) { // timeout | ||
116 | break; | ||
117 | } | ||
118 | |||
119 | if (FD_ISSET(sock, &wfds) && dest < last) { | ||
120 | // configure layer2 socket address information | ||
121 | struct sockaddr_ll addr; | ||
122 | memset(&addr, 0, sizeof(addr)); | ||
123 | if ((addr.sll_ifindex = if_nametoindex(dev)) == 0) | ||
124 | errExit("if_nametoindex"); | ||
125 | addr.sll_family = AF_PACKET; | ||
126 | memcpy (addr.sll_addr, mac, 6); | ||
127 | addr.sll_halen = htons(6); | ||
128 | |||
129 | // build the arp packet header | ||
130 | ArpHdr hdr; | ||
131 | memset(&hdr, 0, sizeof(hdr)); | ||
132 | hdr.htype = htons(1); | ||
133 | hdr.ptype = htons(ETH_P_IP); | ||
134 | hdr.hlen = 6; | ||
135 | hdr.plen = 4; | ||
136 | hdr.opcode = htons(1); //ARPOP_REQUEST | ||
137 | memcpy(hdr.sender_mac, mac, 6); | ||
138 | memcpy(hdr.sender_ip, (uint8_t *)&src, 4); | ||
139 | uint32_t dst = htonl(dest); | ||
140 | memcpy(hdr.target_ip, (uint8_t *)&dst, 4); | ||
141 | |||
142 | // build ethernet frame | ||
143 | uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc | ||
144 | memset(frame, 0, sizeof(frame)); | ||
145 | frame[0] = frame[1] = frame[2] = frame[3] = frame[4] = frame[5] = 0xff; | ||
146 | memcpy(frame + 6, mac, 6); | ||
147 | frame[12] = ETH_P_ARP / 256; | ||
148 | frame[13] = ETH_P_ARP % 256; | ||
149 | memcpy (frame + 14, &hdr, sizeof(hdr)); | ||
150 | |||
151 | // send packet | ||
152 | int len; | ||
153 | if ((len = sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr))) <= 0) | ||
154 | errExit("send"); | ||
155 | //printf("send %d bytes to %d.%d.%d.%d\n", len, PRINT_IP(dest)); | ||
156 | fflush(0); | ||
157 | dest++; | ||
158 | } | ||
159 | |||
160 | if (FD_ISSET(sock, &rfds)) { | ||
161 | // read the incoming packet | ||
162 | int len = recvfrom(sock, frame, ETH_FRAME_LEN, 0, NULL, NULL); | ||
163 | if (len < 0) { | ||
164 | perror("recvfrom"); | ||
165 | } | ||
166 | |||
167 | // parse the incoming packet | ||
168 | if ((unsigned int) len < 14 + sizeof(ArpHdr)) | ||
169 | continue; | ||
170 | |||
171 | // look only at ARP packets | ||
172 | if (frame[12] != (ETH_P_ARP / 256) || frame[13] != (ETH_P_ARP % 256)) | ||
173 | continue; | ||
174 | |||
175 | ArpHdr hdr; | ||
176 | memcpy(&hdr, frame + 14, sizeof(ArpHdr)); | ||
177 | |||
178 | if (hdr.opcode == htons(2)) { | ||
179 | // check my mac and my address | ||
180 | if (memcmp(mac, hdr.target_mac, 6) != 0) | ||
181 | continue; | ||
182 | uint32_t ip; | ||
183 | memcpy(&ip, hdr.target_ip, 4); | ||
184 | if (ip != src) | ||
185 | continue; | ||
186 | memcpy(&ip, hdr.sender_ip, 4); | ||
187 | ip = ntohl(ip); | ||
188 | |||
189 | if (ip == last_ip) // filter duplicates | ||
190 | continue; | ||
191 | last_ip = ip; | ||
192 | |||
193 | // printing | ||
194 | if (header_printed == 0) { | ||
195 | printf(" Network scan:\n"); | ||
196 | header_printed = 1; | ||
197 | } | ||
198 | printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", | ||
199 | PRINT_MAC(hdr.sender_mac), PRINT_IP(ip)); | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | |||
204 | close(sock); | ||
205 | } | ||
206 | |||
207 | |||
208 | |||
diff --git a/src/fnet/fnet.h b/src/fnet/fnet.h new file mode 100644 index 000000000..0c5e5baef --- /dev/null +++ b/src/fnet/fnet.h | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | #ifndef FNET_H | ||
21 | #define FNET_H | ||
22 | |||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <string.h> | ||
26 | #include <assert.h> | ||
27 | #include "../include/common.h" | ||
28 | |||
29 | // veth.c | ||
30 | int net_create_veth(const char *dev, const char *nsdev, unsigned pid); | ||
31 | int net_create_macvlan(const char *dev, const char *parent, unsigned pid); | ||
32 | int net_move_interface(const char *dev, unsigned pid); | ||
33 | |||
34 | // interface.c | ||
35 | void net_bridge_add_interface(const char *bridge, const char *dev); | ||
36 | void net_if_up(const char *ifname); | ||
37 | int net_get_mtu(const char *ifname); | ||
38 | void net_set_mtu(const char *ifname, int mtu); | ||
39 | void net_ifprint(int scan); | ||
40 | int net_get_mac(const char *ifname, unsigned char mac[6]); | ||
41 | void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); | ||
42 | int net_if_mac(const char *ifname, const unsigned char mac[6]); | ||
43 | void net_if_ip6(const char *ifname, const char *addr6); | ||
44 | |||
45 | |||
46 | // arp.c | ||
47 | void arp_scan(const char *dev, uint32_t ifip, uint32_t ifmask); | ||
48 | |||
49 | #endif | ||
diff --git a/src/fnet/interface.c b/src/fnet/interface.c new file mode 100644 index 000000000..3958efddd --- /dev/null +++ b/src/fnet/interface.c | |||
@@ -0,0 +1,370 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | |||
21 | #include "fnet.h" | ||
22 | #include <arpa/inet.h> | ||
23 | #include <sys/socket.h> | ||
24 | #include <sys/ioctl.h> | ||
25 | #include <netdb.h> | ||
26 | #include <ifaddrs.h> | ||
27 | #include <net/if.h> | ||
28 | #include <net/if_arp.h> | ||
29 | #include <net/route.h> | ||
30 | #include <linux/if_bridge.h> | ||
31 | |||
32 | static void check_if_name(const char *ifname) { | ||
33 | if (strlen(ifname) > IFNAMSIZ) { | ||
34 | fprintf(stderr, "Error fnet: invalid network device name %s\n", ifname); | ||
35 | exit(1); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | // add a veth device to a bridge | ||
40 | void net_bridge_add_interface(const char *bridge, const char *dev) { | ||
41 | check_if_name(bridge); | ||
42 | check_if_name(dev); | ||
43 | |||
44 | // somehow adding the interface to the bridge resets MTU on bridge device!!! | ||
45 | // workaround: restore MTU on the bridge device | ||
46 | // todo: put a real fix in | ||
47 | int mtu1 = net_get_mtu(bridge); | ||
48 | |||
49 | struct ifreq ifr; | ||
50 | int err; | ||
51 | int ifindex = if_nametoindex(dev); | ||
52 | |||
53 | if (ifindex <= 0) | ||
54 | errExit("if_nametoindex"); | ||
55 | |||
56 | int sock; | ||
57 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) | ||
58 | errExit("socket"); | ||
59 | |||
60 | memset(&ifr, 0, sizeof(ifr)); | ||
61 | strncpy(ifr.ifr_name, bridge, IFNAMSIZ); | ||
62 | #ifdef SIOCBRADDIF | ||
63 | ifr.ifr_ifindex = ifindex; | ||
64 | err = ioctl(sock, SIOCBRADDIF, &ifr); | ||
65 | if (err < 0) | ||
66 | #endif | ||
67 | { | ||
68 | unsigned long args[4] = { BRCTL_ADD_IF, ifindex, 0, 0 }; | ||
69 | |||
70 | ifr.ifr_data = (char *) args; | ||
71 | err = ioctl(sock, SIOCDEVPRIVATE, &ifr); | ||
72 | } | ||
73 | (void) err; | ||
74 | close(sock); | ||
75 | |||
76 | int mtu2 = net_get_mtu(bridge); | ||
77 | if (mtu1 != mtu2) | ||
78 | net_set_mtu(bridge, mtu1); | ||
79 | } | ||
80 | |||
81 | |||
82 | // bring interface up | ||
83 | void net_if_up(const char *ifname) { | ||
84 | check_if_name(ifname); | ||
85 | |||
86 | int sock = socket(AF_INET,SOCK_DGRAM,0); | ||
87 | if (sock < 0) | ||
88 | errExit("socket"); | ||
89 | |||
90 | // get the existing interface flags | ||
91 | struct ifreq ifr; | ||
92 | memset(&ifr, 0, sizeof(ifr)); | ||
93 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
94 | ifr.ifr_addr.sa_family = AF_INET; | ||
95 | |||
96 | // read the existing flags | ||
97 | if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) | ||
98 | errExit("ioctl"); | ||
99 | |||
100 | ifr.ifr_flags |= IFF_UP; | ||
101 | |||
102 | // set the new flags | ||
103 | if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) | ||
104 | errExit("ioctl"); | ||
105 | |||
106 | // checking | ||
107 | // read the existing flags | ||
108 | if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) | ||
109 | errExit("ioctl"); | ||
110 | |||
111 | // wait not more than 500ms for the interface to come up | ||
112 | int cnt = 0; | ||
113 | while (cnt < 50) { | ||
114 | usleep(10000); // sleep 10ms | ||
115 | |||
116 | // read the existing flags | ||
117 | if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) | ||
118 | errExit("ioctl"); | ||
119 | if (ifr.ifr_flags & IFF_RUNNING) | ||
120 | break; | ||
121 | cnt++; | ||
122 | } | ||
123 | |||
124 | close(sock); | ||
125 | } | ||
126 | |||
127 | int net_get_mtu(const char *ifname) { | ||
128 | check_if_name(ifname); | ||
129 | int mtu = 0; | ||
130 | int s; | ||
131 | struct ifreq ifr; | ||
132 | |||
133 | if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) | ||
134 | errExit("socket"); | ||
135 | |||
136 | memset(&ifr, 0, sizeof(ifr)); | ||
137 | ifr.ifr_addr.sa_family = AF_INET; | ||
138 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
139 | if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0) | ||
140 | mtu = ifr.ifr_mtu; | ||
141 | close(s); | ||
142 | |||
143 | |||
144 | return mtu; | ||
145 | } | ||
146 | |||
147 | void net_set_mtu(const char *ifname, int mtu) { | ||
148 | check_if_name(ifname); | ||
149 | int s; | ||
150 | struct ifreq ifr; | ||
151 | |||
152 | if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) | ||
153 | errExit("socket"); | ||
154 | |||
155 | memset(&ifr, 0, sizeof(ifr)); | ||
156 | ifr.ifr_addr.sa_family = AF_INET; | ||
157 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
158 | ifr.ifr_mtu = mtu; | ||
159 | if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) != 0) | ||
160 | fprintf(stderr, "Warning fnet: cannot set mtu for interface %s\n", ifname); | ||
161 | close(s); | ||
162 | } | ||
163 | |||
164 | // scan interfaces in current namespace and print IP address/mask for each interface | ||
165 | void net_ifprint(int scan) { | ||
166 | uint32_t ip; | ||
167 | uint32_t mask; | ||
168 | struct ifaddrs *ifaddr, *ifa; | ||
169 | |||
170 | if (getifaddrs(&ifaddr) == -1) | ||
171 | errExit("getifaddrs"); | ||
172 | |||
173 | printf("%-17.17s%-19.19s%-17.17s%-17.17s%-6.6s\n", | ||
174 | "Interface", "MAC", "IP", "Mask", "Status"); | ||
175 | // walk through the linked list | ||
176 | for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { | ||
177 | if (ifa->ifa_addr == NULL) | ||
178 | continue; | ||
179 | |||
180 | if (ifa->ifa_addr->sa_family == AF_INET) { | ||
181 | struct sockaddr_in *si = (struct sockaddr_in *) ifa->ifa_netmask; | ||
182 | mask = ntohl(si->sin_addr.s_addr); | ||
183 | si = (struct sockaddr_in *) ifa->ifa_addr; | ||
184 | ip = ntohl(si->sin_addr.s_addr); | ||
185 | |||
186 | // interface status | ||
187 | char *status; | ||
188 | if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) | ||
189 | status = "UP"; | ||
190 | else | ||
191 | status = "DOWN"; | ||
192 | |||
193 | // ip address and mask | ||
194 | char ipstr[30]; | ||
195 | sprintf(ipstr, "%d.%d.%d.%d", PRINT_IP(ip)); | ||
196 | char maskstr[30]; | ||
197 | sprintf(maskstr, "%d.%d.%d.%d", PRINT_IP(mask)); | ||
198 | |||
199 | // mac address | ||
200 | unsigned char mac[6]; | ||
201 | net_get_mac(ifa->ifa_name, mac); | ||
202 | char macstr[30]; | ||
203 | if (strcmp(ifa->ifa_name, "lo") == 0) | ||
204 | macstr[0] = '\0'; | ||
205 | else | ||
206 | sprintf(macstr, "%02x:%02x:%02x:%02x:%02x:%02x", PRINT_MAC(mac)); | ||
207 | |||
208 | |||
209 | printf("%-17.17s%-19.19s%-17.17s%-17.17s%-6.6s\n", | ||
210 | ifa->ifa_name, macstr, ipstr, maskstr, status); | ||
211 | |||
212 | // network scanning | ||
213 | if (!scan) // scanning disabled | ||
214 | continue; | ||
215 | if (strcmp(ifa->ifa_name, "lo") == 0) // no loopbabck scanning | ||
216 | continue; | ||
217 | if (mask2bits(mask) < 16) // not scanning large networks | ||
218 | continue; | ||
219 | if (!ip) // if not configured | ||
220 | continue; | ||
221 | // only if the interface is up and running | ||
222 | if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) | ||
223 | arp_scan(ifa->ifa_name, ip, mask); | ||
224 | } | ||
225 | } | ||
226 | freeifaddrs(ifaddr); | ||
227 | } | ||
228 | |||
229 | int net_get_mac(const char *ifname, unsigned char mac[6]) { | ||
230 | check_if_name(ifname); | ||
231 | |||
232 | struct ifreq ifr; | ||
233 | int sock; | ||
234 | |||
235 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) | ||
236 | errExit("socket"); | ||
237 | |||
238 | memset(&ifr, 0, sizeof(ifr)); | ||
239 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
240 | ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; | ||
241 | |||
242 | if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) | ||
243 | errExit("ioctl"); | ||
244 | memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); | ||
245 | |||
246 | close(sock); | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | // configure interface ipv4 address | ||
251 | void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu) { | ||
252 | check_if_name(ifname); | ||
253 | int sock = socket(AF_INET,SOCK_DGRAM,0); | ||
254 | if (sock < 0) | ||
255 | errExit("socket"); | ||
256 | |||
257 | struct ifreq ifr; | ||
258 | memset(&ifr, 0, sizeof(ifr)); | ||
259 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
260 | ifr.ifr_addr.sa_family = AF_INET; | ||
261 | |||
262 | ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip); | ||
263 | if (ioctl( sock, SIOCSIFADDR, &ifr ) < 0) | ||
264 | errExit("ioctl"); | ||
265 | |||
266 | if (ip != 0) { | ||
267 | ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(mask); | ||
268 | if (ioctl( sock, SIOCSIFNETMASK, &ifr ) < 0) | ||
269 | errExit("ioctl"); | ||
270 | } | ||
271 | |||
272 | // configure mtu | ||
273 | if (mtu > 0) { | ||
274 | ifr.ifr_mtu = mtu; | ||
275 | if (ioctl( sock, SIOCSIFMTU, &ifr ) < 0) | ||
276 | errExit("ioctl"); | ||
277 | } | ||
278 | |||
279 | close(sock); | ||
280 | usleep(10000); // sleep 10ms | ||
281 | return; | ||
282 | } | ||
283 | |||
284 | int net_if_mac(const char *ifname, const unsigned char mac[6]) { | ||
285 | check_if_name(ifname); | ||
286 | struct ifreq ifr; | ||
287 | int sock; | ||
288 | |||
289 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) | ||
290 | errExit("socket"); | ||
291 | |||
292 | memset(&ifr, 0, sizeof(ifr)); | ||
293 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
294 | ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; | ||
295 | memcpy(ifr.ifr_hwaddr.sa_data, mac, 6); | ||
296 | |||
297 | if (ioctl(sock, SIOCSIFHWADDR, &ifr) == -1) | ||
298 | errExit("ioctl"); | ||
299 | close(sock); | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | // configure interface ipv6 address | ||
304 | // ex: firejail --net=eth0 --ip6=2001:0db8:0:f101::1/64 | ||
305 | struct ifreq6 { | ||
306 | struct in6_addr ifr6_addr; | ||
307 | uint32_t ifr6_prefixlen; | ||
308 | unsigned int ifr6_ifindex; | ||
309 | }; | ||
310 | void net_if_ip6(const char *ifname, const char *addr6) { | ||
311 | check_if_name(ifname); | ||
312 | if (strchr(addr6, ':') == NULL) { | ||
313 | fprintf(stderr, "Error fnet: invalid IPv6 address %s\n", addr6); | ||
314 | exit(1); | ||
315 | } | ||
316 | |||
317 | // extract prefix | ||
318 | unsigned long prefix; | ||
319 | char *ptr; | ||
320 | if ((ptr = strchr(addr6, '/'))) { | ||
321 | prefix = atol(ptr + 1); | ||
322 | if (prefix > 128) { | ||
323 | fprintf(stderr, "Error fnet: invalid prefix for IPv6 address %s\n", addr6); | ||
324 | exit(1); | ||
325 | } | ||
326 | *ptr = '\0'; // mark the end of the address | ||
327 | } | ||
328 | else | ||
329 | prefix = 128; | ||
330 | |||
331 | // extract address | ||
332 | struct sockaddr_in6 sin6; | ||
333 | memset(&sin6, 0, sizeof(sin6)); | ||
334 | sin6.sin6_family = AF_INET6; | ||
335 | int rv = inet_pton(AF_INET6, addr6, sin6.sin6_addr.s6_addr); | ||
336 | if (rv <= 0) { | ||
337 | fprintf(stderr, "Error fnet: invalid IPv6 address %s\n", addr6); | ||
338 | exit(1); | ||
339 | } | ||
340 | |||
341 | // open socket | ||
342 | int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); | ||
343 | if (sock < 0) { | ||
344 | fprintf(stderr, "Error fnet: IPv6 is not supported on this system\n"); | ||
345 | exit(1); | ||
346 | } | ||
347 | |||
348 | // find interface index | ||
349 | struct ifreq ifr; | ||
350 | memset(&ifr, 0, sizeof(ifr)); | ||
351 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
352 | ifr.ifr_addr.sa_family = AF_INET; | ||
353 | if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) { | ||
354 | perror("ioctl SIOGIFINDEX"); | ||
355 | exit(1); | ||
356 | } | ||
357 | |||
358 | // configure address | ||
359 | struct ifreq6 ifr6; | ||
360 | memset(&ifr6, 0, sizeof(ifr6)); | ||
361 | ifr6.ifr6_prefixlen = prefix; | ||
362 | ifr6.ifr6_ifindex = ifr.ifr_ifindex; | ||
363 | memcpy((char *) &ifr6.ifr6_addr, (char *) &sin6.sin6_addr, sizeof(struct in6_addr)); | ||
364 | if (ioctl(sock, SIOCSIFADDR, &ifr6) < 0) { | ||
365 | perror("ioctl SIOCSIFADDR"); | ||
366 | exit(1); | ||
367 | } | ||
368 | |||
369 | close(sock); | ||
370 | } | ||
diff --git a/src/fnet/main.c b/src/fnet/main.c new file mode 100644 index 000000000..4ae9eb6e3 --- /dev/null +++ b/src/fnet/main.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fnet.h" | ||
21 | |||
22 | static void usage(void) { | ||
23 | printf("Usage:\n"); | ||
24 | printf("\tfnet create veth dev1 dev2 bridge child\n"); | ||
25 | printf("\tfnet create macvlan dev parent child\n"); | ||
26 | printf("\tfnet moveif dev proc\n"); | ||
27 | printf("\tfnet printif\n"); | ||
28 | printf("\tfnet printif scan\n"); | ||
29 | printf("\tfnet config interface dev ip mask mtu\n"); | ||
30 | printf("\tfnet config mac addr\n"); | ||
31 | printf("\tfnet config ipv6 dev ipn"); | ||
32 | printf("\tfmet ifup dev\n"); | ||
33 | } | ||
34 | |||
35 | int main(int argc, char **argv) { | ||
36 | #if 0 | ||
37 | { | ||
38 | //system("cat /proc/self/status"); | ||
39 | int i; | ||
40 | for (i = 0; i < argc; i++) | ||
41 | printf("*%s* ", argv[i]); | ||
42 | printf("\n"); | ||
43 | } | ||
44 | #endif | ||
45 | if (argc < 2) | ||
46 | return 1; | ||
47 | |||
48 | |||
49 | |||
50 | if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { | ||
51 | usage(); | ||
52 | return 0; | ||
53 | } | ||
54 | else if (argc == 3 && strcmp(argv[1], "ifup") == 0) { | ||
55 | net_if_up(argv[2]); | ||
56 | } | ||
57 | else if (argc == 2 && strcmp(argv[1], "printif") == 0) { | ||
58 | net_ifprint(0); | ||
59 | } | ||
60 | else if (argc == 3 && strcmp(argv[1], "printif") == 0 && strcmp(argv[2], "scan") == 0) { | ||
61 | net_ifprint(1); | ||
62 | } | ||
63 | else if (argc == 7 && strcmp(argv[1], "create") == 0 && strcmp(argv[2], "veth") == 0) { | ||
64 | // create veth pair and move one end in the the namespace | ||
65 | net_create_veth(argv[3], argv[4], atoi(argv[6])); | ||
66 | // connect the ohter veth end to the bridge ... | ||
67 | net_bridge_add_interface(argv[5], argv[3]); | ||
68 | // ... and bring it up | ||
69 | net_if_up(argv[3]); | ||
70 | } | ||
71 | else if (argc == 6 && strcmp(argv[1], "create") == 0 && strcmp(argv[2], "macvlan") == 0) { | ||
72 | net_create_macvlan(argv[3], argv[4], atoi(argv[5])); | ||
73 | } | ||
74 | else if (argc == 7 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "interface") == 0) { | ||
75 | char *dev = argv[3]; | ||
76 | uint32_t ip = (uint32_t) atoll(argv[4]); | ||
77 | uint32_t mask = (uint32_t) atoll(argv[5]); | ||
78 | int mtu = atoi(argv[6]); | ||
79 | // configure interface | ||
80 | net_if_ip(dev, ip, mask, mtu); | ||
81 | // ... and bring it up | ||
82 | net_if_up(dev); | ||
83 | } | ||
84 | else if (argc == 5 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "mac") == 0) { | ||
85 | unsigned char mac[6]; | ||
86 | if (atomac(argv[4], mac)) { | ||
87 | fprintf(stderr, "Error fnet: invalid mac address %s\n", argv[4]); | ||
88 | } | ||
89 | net_if_mac(argv[3], mac); | ||
90 | } | ||
91 | else if (argc == 4 && strcmp(argv[1], "moveif") == 0) { | ||
92 | net_move_interface(argv[2], atoi(argv[3])); | ||
93 | } | ||
94 | else if (argc == 5 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "ipv6") == 0) { | ||
95 | net_if_ip6(argv[3], argv[4]); | ||
96 | } | ||
97 | else { | ||
98 | fprintf(stderr, "Error fnet: invalid arguments\n"); | ||
99 | return 1; | ||
100 | } | ||
101 | |||
102 | return 0; | ||
103 | } | ||
diff --git a/src/firejail/veth.c b/src/fnet/veth.c index df3c1d1f9..546fafcec 100644 --- a/src/firejail/veth.c +++ b/src/fnet/veth.c | |||
@@ -45,7 +45,7 @@ | |||
45 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 45 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
46 | */ | 46 | */ |
47 | 47 | ||
48 | #include "firejail.h" | 48 | #include "fnet.h" |
49 | #include "../include/libnetlink.h" | 49 | #include "../include/libnetlink.h" |
50 | #include <linux/veth.h> | 50 | #include <linux/veth.h> |
51 | #include <net/if.h> | 51 | #include <net/if.h> |
@@ -63,8 +63,6 @@ int net_create_veth(const char *dev, const char *nsdev, unsigned pid) { | |||
63 | int len; | 63 | int len; |
64 | struct iplink_req req; | 64 | struct iplink_req req; |
65 | 65 | ||
66 | if (arg_debug) | ||
67 | printf("create veth %s/%s/%u\n", dev, nsdev, pid); | ||
68 | assert(dev); | 66 | assert(dev); |
69 | assert(nsdev); | 67 | assert(nsdev); |
70 | assert(pid); | 68 | assert(pid); |
@@ -113,6 +111,8 @@ int net_create_veth(const char *dev, const char *nsdev, unsigned pid) { | |||
113 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) | 111 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) |
114 | exit(2); | 112 | exit(2); |
115 | 113 | ||
114 | rtnl_close(&rth); | ||
115 | |||
116 | return 0; | 116 | return 0; |
117 | } | 117 | } |
118 | 118 | ||
@@ -120,8 +120,6 @@ int net_create_veth(const char *dev, const char *nsdev, unsigned pid) { | |||
120 | int net_create_macvlan(const char *dev, const char *parent, unsigned pid) { | 120 | int net_create_macvlan(const char *dev, const char *parent, unsigned pid) { |
121 | int len; | 121 | int len; |
122 | struct iplink_req req; | 122 | struct iplink_req req; |
123 | if (arg_debug) | ||
124 | printf("create macvlan %s, parent %s\n", dev, parent); | ||
125 | assert(dev); | 123 | assert(dev); |
126 | assert(parent); | 124 | assert(parent); |
127 | 125 | ||
@@ -177,6 +175,8 @@ int net_create_macvlan(const char *dev, const char *parent, unsigned pid) { | |||
177 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) | 175 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) |
178 | exit(2); | 176 | exit(2); |
179 | 177 | ||
178 | rtnl_close(&rth); | ||
179 | |||
180 | return 0; | 180 | return 0; |
181 | } | 181 | } |
182 | 182 | ||
@@ -184,8 +184,6 @@ int net_create_macvlan(const char *dev, const char *parent, unsigned pid) { | |||
184 | // when the interface is moved, netlink does not preserve interface configuration | 184 | // when the interface is moved, netlink does not preserve interface configuration |
185 | int net_move_interface(const char *dev, unsigned pid) { | 185 | int net_move_interface(const char *dev, unsigned pid) { |
186 | struct iplink_req req; | 186 | struct iplink_req req; |
187 | if (arg_debug) | ||
188 | printf("move device %s inside the namespace\n", dev); | ||
189 | assert(dev); | 187 | assert(dev); |
190 | 188 | ||
191 | if (rtnl_open(&rth, 0) < 0) { | 189 | if (rtnl_open(&rth, 0) < 0) { |
@@ -215,6 +213,8 @@ int net_move_interface(const char *dev, unsigned pid) { | |||
215 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) | 213 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) |
216 | exit(2); | 214 | exit(2); |
217 | 215 | ||
216 | rtnl_close(&rth); | ||
217 | |||
218 | return 0; | 218 | return 0; |
219 | } | 219 | } |
220 | 220 | ||
diff --git a/src/fseccomp/Makefile.in b/src/fseccomp/Makefile.in new file mode 100644 index 000000000..04c46f128 --- /dev/null +++ b/src/fseccomp/Makefile.in | |||
@@ -0,0 +1,45 @@ | |||
1 | all: fseccomp | ||
2 | |||
3 | prefix=@prefix@ | ||
4 | exec_prefix=@exec_prefix@ | ||
5 | libdir=@libdir@ | ||
6 | sysconfdir=@sysconfdir@ | ||
7 | |||
8 | VERSION=@PACKAGE_VERSION@ | ||
9 | NAME=@PACKAGE_NAME@ | ||
10 | HAVE_SECCOMP_H=@HAVE_SECCOMP_H@ | ||
11 | HAVE_SECCOMP=@HAVE_SECCOMP@ | ||
12 | HAVE_CHROOT=@HAVE_CHROOT@ | ||
13 | HAVE_BIND=@HAVE_BIND@ | ||
14 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ | ||
15 | HAVE_NETWORK=@HAVE_NETWORK@ | ||
16 | HAVE_USERNS=@HAVE_USERNS@ | ||
17 | HAVE_X11=@HAVE_X11@ | ||
18 | HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ | ||
19 | HAVE_WHITELIST=@HAVE_WHITELIST@ | ||
20 | HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ | ||
21 | HAVE_APPARMOR=@HAVE_APPARMOR@ | ||
22 | HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ | ||
23 | HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@ | ||
24 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
25 | HAVE_GCOV=@HAVE_GCOV@ | ||
26 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
27 | |||
28 | H_FILE_LIST = $(sort $(wildcard *.[h])) | ||
29 | C_FILE_LIST = $(sort $(wildcard *.c)) | ||
30 | OBJS = $(C_FILE_LIST:.c=.o) | ||
31 | BINOBJS = $(foreach file, $(OBJS), $file) | ||
32 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | ||
33 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | ||
34 | |||
35 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h | ||
36 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | ||
37 | |||
38 | fseccomp: $(OBJS) | ||
39 | $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) | ||
40 | |||
41 | clean:; rm -f *.o fseccomp *.gcov *.gcda *.gcno | ||
42 | |||
43 | distclean: clean | ||
44 | rm -fr Makefile | ||
45 | |||
diff --git a/src/firejail/errno.c b/src/fseccomp/errno.c index c493dfa09..dbee916d4 100644 --- a/src/firejail/errno.c +++ b/src/fseccomp/errno.c | |||
@@ -17,9 +17,8 @@ | |||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | 17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | #include "fseccomp.h" | ||
20 | 21 | ||
21 | #ifdef HAVE_SECCOMP | ||
22 | #include "firejail.h" | ||
23 | #include <errno.h> | 22 | #include <errno.h> |
24 | //#include <attr/xattr.h> | 23 | //#include <attr/xattr.h> |
25 | 24 | ||
@@ -171,20 +170,7 @@ static ErrnoEntry errnolist[] = { | |||
171 | #endif | 170 | #endif |
172 | }; | 171 | }; |
173 | 172 | ||
174 | int errno_highest_nr(void) { | ||
175 | int i, max = 0; | ||
176 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | ||
177 | for (i = 0; i < elems; i++) { | ||
178 | if (errnolist[i].nr > max) | ||
179 | max = errnolist[i].nr; | ||
180 | } | ||
181 | |||
182 | return max; | ||
183 | } | ||
184 | |||
185 | int errno_find_name(const char *name) { | 173 | int errno_find_name(const char *name) { |
186 | EUID_ASSERT(); | ||
187 | |||
188 | int i; | 174 | int i; |
189 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | 175 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); |
190 | for (i = 0; i < elems; i++) { | 176 | for (i = 0; i < elems; i++) { |
@@ -206,9 +192,9 @@ char *errno_find_nr(int nr) { | |||
206 | return "unknown"; | 192 | return "unknown"; |
207 | } | 193 | } |
208 | 194 | ||
195 | |||
196 | |||
209 | void errno_print(void) { | 197 | void errno_print(void) { |
210 | EUID_ASSERT(); | ||
211 | |||
212 | int i; | 198 | int i; |
213 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | 199 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); |
214 | for (i = 0; i < elems; i++) { | 200 | for (i = 0; i < elems; i++) { |
@@ -216,5 +202,3 @@ void errno_print(void) { | |||
216 | } | 202 | } |
217 | printf("\n"); | 203 | printf("\n"); |
218 | } | 204 | } |
219 | |||
220 | #endif // HAVE_SECCOMP | ||
diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h new file mode 100644 index 000000000..504f1c23f --- /dev/null +++ b/src/fseccomp/fseccomp.h | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | #ifndef FSECCOMP_H | ||
21 | #define FSECCOMP_H | ||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <assert.h> | ||
26 | #include "../include/common.h" | ||
27 | |||
28 | // syscall.c | ||
29 | void syscall_print(void); | ||
30 | int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall, int arg), int fd, int arg); | ||
31 | int syscall_find_name(const char *name); | ||
32 | char *syscall_find_nr(int nr); | ||
33 | |||
34 | // errno.c | ||
35 | void errno_print(void); | ||
36 | int errno_find_name(const char *name); | ||
37 | char *errno_find_nr(int nr); | ||
38 | |||
39 | // protocol.c | ||
40 | void protocol_print(void); | ||
41 | void protocol_build_filter(const char *prlist, const char *fname); | ||
42 | |||
43 | // seccomp_secondary.c | ||
44 | void seccomp_secondary_64(const char *fname); | ||
45 | void seccomp_secondary_32(const char *fname); | ||
46 | |||
47 | // seccomp_file.c | ||
48 | void filter_init(int fd); | ||
49 | void filter_add_whitelist(int fd, int syscall, int arg); | ||
50 | void filter_add_blacklist(int fd, int syscall, int arg); | ||
51 | void filter_add_errno(int fd, int syscall, int arg); | ||
52 | void filter_end_blacklist(int fd); | ||
53 | void filter_end_whitelist(int fd); | ||
54 | |||
55 | // seccomp.c | ||
56 | // default list | ||
57 | void seccomp_default(const char *fname, int allow_debuggers); | ||
58 | // drop list | ||
59 | void seccomp_drop(const char *fname, char *list, int allow_debuggers); | ||
60 | // default+drop list | ||
61 | void seccomp_default_drop(const char *fname, char *list, int allow_debuggers); | ||
62 | // whitelisted filter | ||
63 | void seccomp_keep(const char *fname, char *list); | ||
64 | |||
65 | // seccomp_print | ||
66 | void filter_print(const char *fname); | ||
67 | |||
68 | #endif | ||
diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c new file mode 100644 index 000000000..39e72fdf9 --- /dev/null +++ b/src/fseccomp/main.c | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | |||
22 | static void usage(void) { | ||
23 | printf("Usage:\n"); | ||
24 | printf("\tfseccomp debug-syscalls\n"); | ||
25 | printf("\tfseccomp debug-errnos\n"); | ||
26 | printf("\tfseccomp debug-protocols\n"); | ||
27 | printf("\tfseccomp protocol build list file\n"); | ||
28 | printf("\tfseccomp secondary 64 file\n"); | ||
29 | printf("\tfseccomp secondary 32 file\n"); | ||
30 | printf("\tfseccomp default file\n"); | ||
31 | printf("\tfseccomp default file allow-debuggers\n"); | ||
32 | printf("\tfseccomp drop file list\n"); | ||
33 | printf("\tfseccomp drop file list allow-debuggers\n"); | ||
34 | printf("\tfseccomp default drop file list\n"); | ||
35 | printf("\tfseccomp default drop file list allow-debuggers\n"); | ||
36 | printf("\tfseccomp keep file list\n"); | ||
37 | printf("\tfseccomp print file\n"); | ||
38 | } | ||
39 | |||
40 | int main(int argc, char **argv) { | ||
41 | #if 0 | ||
42 | { | ||
43 | //system("cat /proc/self/status"); | ||
44 | int i; | ||
45 | for (i = 0; i < argc; i++) | ||
46 | printf("*%s* ", argv[i]); | ||
47 | printf("\n"); | ||
48 | } | ||
49 | #endif | ||
50 | if (argc < 2) | ||
51 | return 1; | ||
52 | |||
53 | if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { | ||
54 | usage(); | ||
55 | return 0; | ||
56 | } | ||
57 | else if (argc == 2 && strcmp(argv[1], "debug-syscalls") == 0) | ||
58 | syscall_print(); | ||
59 | else if (argc == 2 && strcmp(argv[1], "debug-errnos") == 0) | ||
60 | errno_print(); | ||
61 | else if (argc == 2 && strcmp(argv[1], "debug-protocols") == 0) | ||
62 | protocol_print(); | ||
63 | else if (argc == 5 && strcmp(argv[1], "protocol") == 0 && strcmp(argv[2], "build") == 0) | ||
64 | protocol_build_filter(argv[3], argv[4]); | ||
65 | else if (argc == 4 && strcmp(argv[1], "secondary") == 0 && strcmp(argv[2], "64") == 0) | ||
66 | seccomp_secondary_64(argv[3]); | ||
67 | else if (argc == 4 && strcmp(argv[1], "secondary") == 0 && strcmp(argv[2], "32") == 0) | ||
68 | seccomp_secondary_32(argv[3]); | ||
69 | else if (argc == 3 && strcmp(argv[1], "default") == 0) | ||
70 | seccomp_default(argv[2], 0); | ||
71 | else if (argc == 4 && strcmp(argv[1], "default") == 0 && strcmp(argv[3], "allow-debuggers") == 0) | ||
72 | seccomp_default(argv[2], 1); | ||
73 | else if (argc == 4 && strcmp(argv[1], "drop") == 0) | ||
74 | seccomp_drop(argv[2], argv[3], 0); | ||
75 | else if (argc == 5 && strcmp(argv[1], "drop") == 0 && strcmp(argv[4], "allow-debuggers") == 0) | ||
76 | seccomp_drop(argv[2], argv[3], 1); | ||
77 | else if (argc == 5 && strcmp(argv[1], "default") == 0 && strcmp(argv[2], "drop") == 0) | ||
78 | seccomp_default_drop(argv[3], argv[4], 0); | ||
79 | else if (argc == 6 && strcmp(argv[1], "default") == 0 && strcmp(argv[2], "drop") == 0 && strcmp(argv[5], "allow-debuggers") == 0) | ||
80 | seccomp_default_drop(argv[3], argv[4], 1); | ||
81 | else if (argc == 4 && strcmp(argv[1], "keep") == 0) | ||
82 | seccomp_keep(argv[2], argv[3]); | ||
83 | else if (argc == 3 && strcmp(argv[1], "print") == 0) | ||
84 | filter_print(argv[2]); | ||
85 | else { | ||
86 | fprintf(stderr, "Error fseccomp: invalid arguments\n"); | ||
87 | return 1; | ||
88 | } | ||
89 | |||
90 | return 0; | ||
91 | } \ No newline at end of file | ||
diff --git a/src/fseccomp/protocol.c b/src/fseccomp/protocol.c new file mode 100644 index 000000000..7bf560fe1 --- /dev/null +++ b/src/fseccomp/protocol.c | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | |||
21 | /* | ||
22 | struct sock_filter filter[] = { | ||
23 | VALIDATE_ARCHITECTURE, | ||
24 | EXAMINE_SYSCALL, | ||
25 | ONLY(SYS_socket), | ||
26 | EXAMINE_ARGUMENT(0), // allow only AF_INET and AF_INET6, drop everything else | ||
27 | WHITELIST(AF_INET), | ||
28 | WHITELIST(AF_INET6), | ||
29 | WHITELIST(AF_PACKET), | ||
30 | RETURN_ERRNO(ENOTSUP) | ||
31 | }; | ||
32 | struct sock_fprog prog = { | ||
33 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | ||
34 | .filter = filter, | ||
35 | }; | ||
36 | |||
37 | |||
38 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
39 | perror("prctl(NO_NEW_PRIVS)"); | ||
40 | return 1; | ||
41 | } | ||
42 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | ||
43 | perror("prctl"); | ||
44 | return 1; | ||
45 | } | ||
46 | */ | ||
47 | |||
48 | #include "fseccomp.h" | ||
49 | #include "../include/seccomp.h" | ||
50 | #include <sys/syscall.h> | ||
51 | #include <sys/types.h> | ||
52 | #include <sys/socket.h> | ||
53 | |||
54 | static char *protocol[] = { | ||
55 | "unix", | ||
56 | "inet", | ||
57 | "inet6", | ||
58 | "netlink", | ||
59 | "packet", | ||
60 | NULL | ||
61 | }; | ||
62 | |||
63 | static struct sock_filter protocol_filter_command[] = { | ||
64 | WHITELIST(AF_UNIX), | ||
65 | WHITELIST(AF_INET), | ||
66 | WHITELIST(AF_INET6), | ||
67 | WHITELIST(AF_NETLINK), | ||
68 | WHITELIST(AF_PACKET) | ||
69 | }; | ||
70 | // Note: protocol[] and protocol_filter_command are synchronized | ||
71 | |||
72 | // command length | ||
73 | struct sock_filter whitelist[] = { | ||
74 | WHITELIST(AF_UNIX) | ||
75 | }; | ||
76 | unsigned whitelist_len = sizeof(whitelist) / sizeof(struct sock_filter); | ||
77 | |||
78 | static struct sock_filter *find_protocol_domain(const char *p) { | ||
79 | int i = 0; | ||
80 | while (protocol[i] != NULL) { | ||
81 | if (strcmp(protocol[i], p) == 0) | ||
82 | return &protocol_filter_command[i * whitelist_len]; | ||
83 | i++; | ||
84 | } | ||
85 | |||
86 | return NULL; | ||
87 | } | ||
88 | |||
89 | |||
90 | void protocol_print(void) { | ||
91 | #ifndef SYS_socket | ||
92 | fprintf(stderr, "Warning fseccomp: firejail --protocol not supported on this platform\n"); | ||
93 | return; | ||
94 | #endif | ||
95 | |||
96 | int i = 0; | ||
97 | while (protocol[i] != NULL) { | ||
98 | printf("%s, ", protocol[i]); | ||
99 | i++; | ||
100 | } | ||
101 | printf("\n"); | ||
102 | } | ||
103 | |||
104 | // install protocol filter | ||
105 | void protocol_build_filter(const char *prlist, const char *fname) { | ||
106 | assert(prlist); | ||
107 | assert(fname); | ||
108 | |||
109 | #ifndef SYS_socket | ||
110 | fprintf(stderr, "Warning fseccomp: --protocol not supported on this platform\n"); | ||
111 | return; | ||
112 | #else | ||
113 | // build the filter | ||
114 | struct sock_filter filter[32]; // big enough | ||
115 | memset(&filter[0], 0, sizeof(filter)); | ||
116 | uint8_t *ptr = (uint8_t *) &filter[0]; | ||
117 | |||
118 | // header | ||
119 | struct sock_filter filter_start[] = { | ||
120 | VALIDATE_ARCHITECTURE, | ||
121 | EXAMINE_SYSCALL, | ||
122 | ONLY(SYS_socket), | ||
123 | EXAMINE_ARGUMENT(0) | ||
124 | }; | ||
125 | memcpy(ptr, &filter_start[0], sizeof(filter_start)); | ||
126 | ptr += sizeof(filter_start); | ||
127 | |||
128 | #if 0 | ||
129 | printf("entries %u\n", (unsigned) (sizeof(filter_start) / sizeof(struct sock_filter))); | ||
130 | { | ||
131 | unsigned j; | ||
132 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
133 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
134 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
135 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
136 | printf("%02x, ", (*ptr2) & 0xff); | ||
137 | } | ||
138 | printf("\n"); | ||
139 | } | ||
140 | printf("whitelist_len %u, struct sock_filter len %u\n", whitelist_len, (unsigned) sizeof(struct sock_filter)); | ||
141 | #endif | ||
142 | |||
143 | |||
144 | // parse list and add commands | ||
145 | char *tmplist = strdup(prlist); | ||
146 | if (!tmplist) | ||
147 | errExit("strdup"); | ||
148 | char *token = strtok(tmplist, ","); | ||
149 | if (!token) | ||
150 | errExit("strtok"); | ||
151 | |||
152 | while (token) { | ||
153 | struct sock_filter *domain = find_protocol_domain(token); | ||
154 | if (domain == NULL) { | ||
155 | fprintf(stderr, "Error fseccomp: %s is not a valid protocol\n", token); | ||
156 | exit(1); | ||
157 | } | ||
158 | memcpy(ptr, domain, whitelist_len * sizeof(struct sock_filter)); | ||
159 | ptr += whitelist_len * sizeof(struct sock_filter); | ||
160 | token = strtok(NULL, ","); | ||
161 | |||
162 | #if 0 | ||
163 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
164 | { | ||
165 | unsigned j; | ||
166 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
167 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
168 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
169 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
170 | printf("%02x, ", (*ptr2) & 0xff); | ||
171 | } | ||
172 | printf("\n"); | ||
173 | } | ||
174 | #endif | ||
175 | |||
176 | |||
177 | } | ||
178 | free(tmplist); | ||
179 | |||
180 | // add end of filter | ||
181 | struct sock_filter filter_end[] = { | ||
182 | RETURN_ERRNO(ENOTSUP) | ||
183 | }; | ||
184 | memcpy(ptr, &filter_end[0], sizeof(filter_end)); | ||
185 | ptr += sizeof(filter_end); | ||
186 | |||
187 | #if 0 | ||
188 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
189 | { | ||
190 | unsigned j; | ||
191 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
192 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
193 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
194 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
195 | printf("%02x, ", (*ptr2) & 0xff); | ||
196 | } | ||
197 | printf("\n"); | ||
198 | } | ||
199 | #endif | ||
200 | // save filter to file | ||
201 | int dst = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
202 | if (dst < 0) { | ||
203 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
204 | exit(1); | ||
205 | } | ||
206 | |||
207 | int size = (int) ((uintptr_t) ptr - (uintptr_t) (filter)); | ||
208 | int written = 0; | ||
209 | while (written < size) { | ||
210 | int rv = write(dst, (unsigned char *) filter + written, size - written); | ||
211 | if (rv == -1) { | ||
212 | fprintf(stderr, "Error fseccomp: cannot write %s file\n", fname); | ||
213 | exit(1); | ||
214 | } | ||
215 | written += rv; | ||
216 | } | ||
217 | close(dst); | ||
218 | #endif // SYS_socket | ||
219 | } | ||
diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c new file mode 100644 index 000000000..cc6edc8ca --- /dev/null +++ b/src/fseccomp/seccomp.c | |||
@@ -0,0 +1,292 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | #include "../include/seccomp.h" | ||
22 | #include <sys/syscall.h> | ||
23 | |||
24 | static void add_default_list(int fd, int allow_debuggers) { | ||
25 | #ifdef SYS_mount | ||
26 | filter_add_blacklist(fd, SYS_mount, 0); | ||
27 | #endif | ||
28 | #ifdef SYS_umount2 | ||
29 | filter_add_blacklist(fd, SYS_umount2, 0); | ||
30 | #endif | ||
31 | |||
32 | if (!allow_debuggers) { | ||
33 | #ifdef SYS_ptrace | ||
34 | filter_add_blacklist(fd, SYS_ptrace, 0); | ||
35 | #endif | ||
36 | } | ||
37 | |||
38 | #ifdef SYS_kexec_load | ||
39 | filter_add_blacklist(fd, SYS_kexec_load, 0); | ||
40 | #endif | ||
41 | #ifdef SYS_kexec_file_load | ||
42 | filter_add_blacklist(fd, SYS_kexec_file_load, 0); | ||
43 | #endif | ||
44 | #ifdef SYS_open_by_handle_at | ||
45 | filter_add_blacklist(fd, SYS_open_by_handle_at, 0); | ||
46 | #endif | ||
47 | #ifdef SYS_name_to_handle_at | ||
48 | filter_add_blacklist(fd, SYS_name_to_handle_at, 0); | ||
49 | #endif | ||
50 | #ifdef SYS_init_module | ||
51 | filter_add_blacklist(fd, SYS_init_module, 0); | ||
52 | #endif | ||
53 | #ifdef SYS_finit_module | ||
54 | filter_add_blacklist(fd, SYS_finit_module, 0); | ||
55 | #endif | ||
56 | #ifdef SYS_create_module | ||
57 | filter_add_blacklist(fd, SYS_create_module, 0); | ||
58 | #endif | ||
59 | #ifdef SYS_delete_module | ||
60 | filter_add_blacklist(fd, SYS_delete_module, 0); | ||
61 | #endif | ||
62 | #ifdef SYS_iopl | ||
63 | filter_add_blacklist(fd, SYS_iopl, 0); | ||
64 | #endif | ||
65 | #ifdef SYS_ioperm | ||
66 | filter_add_blacklist(fd, SYS_ioperm, 0); | ||
67 | #endif | ||
68 | #ifdef SYS_ioprio_set | ||
69 | filter_add_blacklist(fd, SYS_ioprio_set, 0); | ||
70 | #endif | ||
71 | #ifdef SYS_ni_syscall | ||
72 | filter_add_blacklist(fd, SYS_ni_syscall, 0); | ||
73 | #endif | ||
74 | #ifdef SYS_swapon | ||
75 | filter_add_blacklist(fd, SYS_swapon, 0); | ||
76 | #endif | ||
77 | #ifdef SYS_swapoff | ||
78 | filter_add_blacklist(fd, SYS_swapoff, 0); | ||
79 | #endif | ||
80 | #ifdef SYS_syslog | ||
81 | filter_add_blacklist(fd, SYS_syslog, 0); | ||
82 | #endif | ||
83 | |||
84 | if (!allow_debuggers) { | ||
85 | #ifdef SYS_process_vm_readv | ||
86 | filter_add_blacklist(fd, SYS_process_vm_readv, 0); | ||
87 | #endif | ||
88 | } | ||
89 | |||
90 | #ifdef SYS_process_vm_writev | ||
91 | filter_add_blacklist(fd, SYS_process_vm_writev, 0); | ||
92 | #endif | ||
93 | |||
94 | // mknod removed in 0.9.29 - it brakes Zotero extension | ||
95 | //#ifdef SYS_mknod | ||
96 | // filter_add_blacklist(SYS_mknod, 0); | ||
97 | //#endif | ||
98 | |||
99 | #ifdef SYS_sysfs | ||
100 | filter_add_blacklist(fd, SYS_sysfs, 0); | ||
101 | #endif | ||
102 | #ifdef SYS__sysctl | ||
103 | filter_add_blacklist(fd, SYS__sysctl, 0); | ||
104 | #endif | ||
105 | #ifdef SYS_adjtimex | ||
106 | filter_add_blacklist(fd, SYS_adjtimex, 0); | ||
107 | #endif | ||
108 | #ifdef SYS_clock_adjtime | ||
109 | filter_add_blacklist(fd, SYS_clock_adjtime, 0); | ||
110 | #endif | ||
111 | #ifdef SYS_lookup_dcookie | ||
112 | filter_add_blacklist(fd, SYS_lookup_dcookie, 0); | ||
113 | #endif | ||
114 | #ifdef SYS_perf_event_open | ||
115 | filter_add_blacklist(fd, SYS_perf_event_open, 0); | ||
116 | #endif | ||
117 | #ifdef SYS_fanotify_init | ||
118 | filter_add_blacklist(fd, SYS_fanotify_init, 0); | ||
119 | #endif | ||
120 | #ifdef SYS_kcmp | ||
121 | filter_add_blacklist(fd, SYS_kcmp, 0); | ||
122 | #endif | ||
123 | #ifdef SYS_add_key | ||
124 | filter_add_blacklist(fd, SYS_add_key, 0); | ||
125 | #endif | ||
126 | #ifdef SYS_request_key | ||
127 | filter_add_blacklist(fd, SYS_request_key, 0); | ||
128 | #endif | ||
129 | #ifdef SYS_keyctl | ||
130 | filter_add_blacklist(fd, SYS_keyctl, 0); | ||
131 | #endif | ||
132 | #ifdef SYS_uselib | ||
133 | filter_add_blacklist(fd, SYS_uselib, 0); | ||
134 | #endif | ||
135 | #ifdef SYS_acct | ||
136 | filter_add_blacklist(fd, SYS_acct, 0); | ||
137 | #endif | ||
138 | #ifdef SYS_modify_ldt | ||
139 | filter_add_blacklist(fd, SYS_modify_ldt, 0); | ||
140 | #endif | ||
141 | #ifdef SYS_pivot_root | ||
142 | filter_add_blacklist(fd, SYS_pivot_root, 0); | ||
143 | #endif | ||
144 | #ifdef SYS_io_setup | ||
145 | filter_add_blacklist(fd, SYS_io_setup, 0); | ||
146 | #endif | ||
147 | #ifdef SYS_io_destroy | ||
148 | filter_add_blacklist(fd, SYS_io_destroy, 0); | ||
149 | #endif | ||
150 | #ifdef SYS_io_getevents | ||
151 | filter_add_blacklist(fd, SYS_io_getevents, 0); | ||
152 | #endif | ||
153 | #ifdef SYS_io_submit | ||
154 | filter_add_blacklist(fd, SYS_io_submit, 0); | ||
155 | #endif | ||
156 | #ifdef SYS_io_cancel | ||
157 | filter_add_blacklist(fd, SYS_io_cancel, 0); | ||
158 | #endif | ||
159 | #ifdef SYS_remap_file_pages | ||
160 | filter_add_blacklist(fd, SYS_remap_file_pages, 0); | ||
161 | #endif | ||
162 | #ifdef SYS_mbind | ||
163 | filter_add_blacklist(fd, SYS_mbind, 0); | ||
164 | #endif | ||
165 | #ifdef SYS_get_mempolicy | ||
166 | filter_add_blacklist(fd, SYS_get_mempolicy, 0); | ||
167 | #endif | ||
168 | #ifdef SYS_set_mempolicy | ||
169 | filter_add_blacklist(fd, SYS_set_mempolicy, 0); | ||
170 | #endif | ||
171 | #ifdef SYS_migrate_pages | ||
172 | filter_add_blacklist(fd, SYS_migrate_pages, 0); | ||
173 | #endif | ||
174 | #ifdef SYS_move_pages | ||
175 | filter_add_blacklist(fd, SYS_move_pages, 0); | ||
176 | #endif | ||
177 | #ifdef SYS_vmsplice | ||
178 | filter_add_blacklist(fd, SYS_vmsplice, 0); | ||
179 | #endif | ||
180 | #ifdef SYS_chroot | ||
181 | filter_add_blacklist(fd, SYS_chroot, 0); | ||
182 | #endif | ||
183 | #ifdef SYS_tuxcall | ||
184 | filter_add_blacklist(fd, SYS_tuxcall, 0); | ||
185 | #endif | ||
186 | #ifdef SYS_reboot | ||
187 | filter_add_blacklist(fd, SYS_reboot, 0); | ||
188 | #endif | ||
189 | #ifdef SYS_nfsservctl | ||
190 | filter_add_blacklist(fd, SYS_nfsservctl, 0); | ||
191 | #endif | ||
192 | #ifdef SYS_get_kernel_syms | ||
193 | filter_add_blacklist(fd, SYS_get_kernel_syms, 0); | ||
194 | #endif | ||
195 | } | ||
196 | |||
197 | // default list | ||
198 | void seccomp_default(const char *fname, int allow_debuggers) { | ||
199 | assert(fname); | ||
200 | |||
201 | // open file | ||
202 | int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
203 | if (fd < 0) { | ||
204 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
205 | exit(1); | ||
206 | } | ||
207 | |||
208 | // build filter | ||
209 | filter_init(fd); | ||
210 | add_default_list(fd, allow_debuggers); | ||
211 | filter_end_blacklist(fd); | ||
212 | |||
213 | // close file | ||
214 | close(fd); | ||
215 | } | ||
216 | |||
217 | // drop list | ||
218 | void seccomp_drop(const char *fname, char *list, int allow_debuggers) { | ||
219 | assert(fname); | ||
220 | (void) allow_debuggers; // todo: to implemnet it | ||
221 | |||
222 | // open file | ||
223 | int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
224 | if (fd < 0) { | ||
225 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
226 | exit(1); | ||
227 | } | ||
228 | |||
229 | // build filter | ||
230 | filter_init(fd); | ||
231 | if (syscall_check_list(list, filter_add_blacklist, fd, 0)) { | ||
232 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | ||
233 | exit(1); | ||
234 | } | ||
235 | filter_end_blacklist(fd); | ||
236 | |||
237 | // close file | ||
238 | close(fd); | ||
239 | } | ||
240 | |||
241 | // default+drop | ||
242 | void seccomp_default_drop(const char *fname, char *list, int allow_debuggers) { | ||
243 | assert(fname); | ||
244 | |||
245 | // open file | ||
246 | int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
247 | if (fd < 0) { | ||
248 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
249 | exit(1); | ||
250 | } | ||
251 | |||
252 | // build filter | ||
253 | filter_init(fd); | ||
254 | add_default_list(fd, allow_debuggers); | ||
255 | if (syscall_check_list(list, filter_add_blacklist, fd, 0)) { | ||
256 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | ||
257 | exit(1); | ||
258 | } | ||
259 | filter_end_blacklist(fd); | ||
260 | |||
261 | // close file | ||
262 | close(fd); | ||
263 | } | ||
264 | |||
265 | void seccomp_keep(const char *fname, char *list) { | ||
266 | // open file | ||
267 | int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
268 | if (fd < 0) { | ||
269 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
270 | exit(1); | ||
271 | } | ||
272 | |||
273 | // build filter | ||
274 | filter_init(fd); | ||
275 | // these 4 syscalls are used by firejail after the seccomp filter is initialized | ||
276 | filter_add_whitelist(fd, SYS_setuid, 0); | ||
277 | filter_add_whitelist(fd, SYS_setgid, 0); | ||
278 | filter_add_whitelist(fd, SYS_setgroups, 0); | ||
279 | filter_add_whitelist(fd, SYS_dup, 0); | ||
280 | filter_add_whitelist(fd, SYS_prctl, 0); | ||
281 | |||
282 | if (syscall_check_list(list, filter_add_whitelist, fd, 0)) { | ||
283 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | ||
284 | exit(1); | ||
285 | } | ||
286 | |||
287 | filter_end_whitelist(fd); | ||
288 | |||
289 | // close file | ||
290 | close(fd); | ||
291 | } | ||
292 | |||
diff --git a/src/fseccomp/seccomp_file.c b/src/fseccomp/seccomp_file.c new file mode 100644 index 000000000..10ef9dd31 --- /dev/null +++ b/src/fseccomp/seccomp_file.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | #include "../include/seccomp.h" | ||
22 | #include <sys/syscall.h> | ||
23 | |||
24 | static void write_to_file(int fd, void *data, int size) { | ||
25 | assert(data); | ||
26 | assert(size); | ||
27 | |||
28 | int written = 0; | ||
29 | while (written < size) { | ||
30 | int rv = write(fd, (unsigned char *) data + written, size - written); | ||
31 | if (rv == -1) { | ||
32 | fprintf(stderr, "Error fseccomp: cannot write seccomp file\n"); | ||
33 | exit(1); | ||
34 | } | ||
35 | written += rv; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | void filter_init(int fd) { | ||
40 | #if defined(__x86_64__) | ||
41 | #define X32_SYSCALL_BIT 0x40000000 | ||
42 | struct sock_filter filter[] = { | ||
43 | VALIDATE_ARCHITECTURE, | ||
44 | EXAMINE_SYSCALL, | ||
45 | // handle X32 ABI | ||
46 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), | ||
47 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), | ||
48 | RETURN_ERRNO(EPERM) | ||
49 | }; | ||
50 | #else | ||
51 | struct sock_filter filter[] = { | ||
52 | VALIDATE_ARCHITECTURE, | ||
53 | EXAMINE_SYSCALL | ||
54 | }; | ||
55 | #endif | ||
56 | |||
57 | #if 0 | ||
58 | { | ||
59 | int i; | ||
60 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
61 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
62 | printf("%x, ", (*ptr) & 0xff); | ||
63 | printf("\n"); | ||
64 | } | ||
65 | #endif | ||
66 | |||
67 | write_to_file(fd, filter, sizeof(filter)); | ||
68 | } | ||
69 | |||
70 | void filter_add_whitelist(int fd, int syscall, int arg) { | ||
71 | (void) arg; | ||
72 | |||
73 | struct sock_filter filter[] = { | ||
74 | WHITELIST(syscall) | ||
75 | }; | ||
76 | write_to_file(fd, filter, sizeof(filter)); | ||
77 | } | ||
78 | |||
79 | void filter_add_blacklist(int fd, int syscall, int arg) { | ||
80 | (void) arg; | ||
81 | |||
82 | struct sock_filter filter[] = { | ||
83 | BLACKLIST(syscall) | ||
84 | }; | ||
85 | write_to_file(fd, filter, sizeof(filter)); | ||
86 | } | ||
87 | |||
88 | void filter_add_errno(int fd, int syscall, int arg) { | ||
89 | struct sock_filter filter[] = { | ||
90 | BLACKLIST_ERRNO(syscall, arg) | ||
91 | }; | ||
92 | write_to_file(fd, filter, sizeof(filter)); | ||
93 | } | ||
94 | |||
95 | void filter_end_blacklist(int fd) { | ||
96 | struct sock_filter filter[] = { | ||
97 | RETURN_ALLOW | ||
98 | }; | ||
99 | write_to_file(fd, filter, sizeof(filter)); | ||
100 | } | ||
101 | |||
102 | void filter_end_whitelist(int fd) { | ||
103 | struct sock_filter filter[] = { | ||
104 | KILL_PROCESS | ||
105 | }; | ||
106 | write_to_file(fd, filter, sizeof(filter)); | ||
107 | } | ||
108 | |||
diff --git a/src/fseccomp/seccomp_print.c b/src/fseccomp/seccomp_print.c new file mode 100644 index 000000000..7dc983b12 --- /dev/null +++ b/src/fseccomp/seccomp_print.c | |||
@@ -0,0 +1,116 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | #include "../include/seccomp.h" | ||
22 | #include <sys/syscall.h> | ||
23 | |||
24 | static struct sock_filter *filter = NULL; | ||
25 | static int filter_cnt = 0; | ||
26 | |||
27 | static void load_seccomp(const char *fname) { | ||
28 | assert(fname); | ||
29 | |||
30 | // check file | ||
31 | struct stat s; | ||
32 | if (stat(fname, &s) == -1) { | ||
33 | fprintf(stderr, "Error fseccomp: cannot read protocol filter file\n"); | ||
34 | exit(1); | ||
35 | } | ||
36 | int size = s.st_size; | ||
37 | unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); | ||
38 | filter_cnt = entries; | ||
39 | //printf("size %d, entries %d\n", s.st_size, entries); | ||
40 | |||
41 | filter = malloc(sizeof(struct sock_filter) * entries); | ||
42 | if (!filter) | ||
43 | errExit("malloc"); | ||
44 | |||
45 | // read filter | ||
46 | memset(filter, 0, sizeof(struct sock_filter) * entries); | ||
47 | int src = open(fname, O_RDONLY); | ||
48 | int rd = 0; | ||
49 | while (rd < size) { | ||
50 | int rv = read(src, (unsigned char *) filter + rd, size - rd); | ||
51 | if (rv == -1) { | ||
52 | fprintf(stderr, "Error fseccomp: cannot read %s file\n", fname); | ||
53 | exit(1); | ||
54 | } | ||
55 | rd += rv; | ||
56 | } | ||
57 | close(src); | ||
58 | } | ||
59 | |||
60 | // debug filter | ||
61 | void filter_print(const char *fname) { | ||
62 | assert(fname); | ||
63 | load_seccomp(fname); | ||
64 | |||
65 | // start filter | ||
66 | struct sock_filter start[] = { | ||
67 | VALIDATE_ARCHITECTURE, | ||
68 | EXAMINE_SYSCALL | ||
69 | }; | ||
70 | |||
71 | // print sizes | ||
72 | printf("SECCOMP Filter:\n"); | ||
73 | |||
74 | // test the start of the filter | ||
75 | if (memcmp(&start[0], filter, sizeof(start)) == 0) { | ||
76 | printf(" VALIDATE_ARCHITECTURE\n"); | ||
77 | printf(" EXAMINE_SYSCAL\n"); | ||
78 | } | ||
79 | else { | ||
80 | printf("Invalid seccomp filter %s\n", fname); | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | // loop trough blacklists | ||
85 | int i = 4; | ||
86 | while (i < filter_cnt) { | ||
87 | // minimal parsing! | ||
88 | unsigned char *ptr = (unsigned char *) &filter[i]; | ||
89 | int *nr = (int *) (ptr + 4); | ||
90 | if (*ptr == 0x15 && *(ptr +14) == 0xff && *(ptr + 15) == 0x7f ) { | ||
91 | printf(" WHITELIST %d %s\n", *nr, syscall_find_nr(*nr)); | ||
92 | i += 2; | ||
93 | } | ||
94 | else if (*ptr == 0x15 && *(ptr +14) == 0 && *(ptr + 15) == 0) { | ||
95 | printf(" BLACKLIST %d %s\n", *nr, syscall_find_nr(*nr)); | ||
96 | i += 2; | ||
97 | } | ||
98 | else if (*ptr == 0x15 && *(ptr +14) == 0x5 && *(ptr + 15) == 0) { | ||
99 | int err = *(ptr + 13) << 8 | *(ptr + 12); | ||
100 | printf(" ERRNO %d %s %d %s\n", *nr, syscall_find_nr(*nr), err, errno_find_nr(err)); | ||
101 | i += 2; | ||
102 | } | ||
103 | else if (*ptr == 0x06 && *(ptr +6) == 0 && *(ptr + 7) == 0 ) { | ||
104 | printf(" KILL_PROCESS\n"); | ||
105 | i++; | ||
106 | } | ||
107 | else if (*ptr == 0x06 && *(ptr +6) == 0xff && *(ptr + 7) == 0x7f ) { | ||
108 | printf(" RETURN_ALLOW\n"); | ||
109 | i++; | ||
110 | } | ||
111 | else { | ||
112 | printf(" UNKNOWN ENTRY!!!\n"); | ||
113 | i++; | ||
114 | } | ||
115 | } | ||
116 | } | ||
diff --git a/src/fseccomp/seccomp_secondary.c b/src/fseccomp/seccomp_secondary.c new file mode 100644 index 000000000..a856e5aef --- /dev/null +++ b/src/fseccomp/seccomp_secondary.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | #include "../include/seccomp.h" | ||
22 | #include <sys/syscall.h> | ||
23 | |||
24 | void seccomp_secondary_64(const char *fname) { | ||
25 | // hardcoded syscall values | ||
26 | struct sock_filter filter[] = { | ||
27 | VALIDATE_ARCHITECTURE_64, | ||
28 | EXAMINE_SYSCALL, | ||
29 | BLACKLIST(165), // mount | ||
30 | BLACKLIST(166), // umount2 | ||
31 | // todo: implement --allow-debuggers | ||
32 | BLACKLIST(101), // ptrace | ||
33 | BLACKLIST(246), // kexec_load | ||
34 | BLACKLIST(304), // open_by_handle_at | ||
35 | BLACKLIST(303), // name_to_handle_at | ||
36 | BLACKLIST(174), // create_module | ||
37 | BLACKLIST(175), // init_module | ||
38 | BLACKLIST(313), // finit_module | ||
39 | BLACKLIST(176), // delete_module | ||
40 | BLACKLIST(172), // iopl | ||
41 | BLACKLIST(173), // ioperm | ||
42 | BLACKLIST(251), // ioprio_set | ||
43 | BLACKLIST(167), // swapon | ||
44 | BLACKLIST(168), // swapoff | ||
45 | BLACKLIST(103), // syslog | ||
46 | BLACKLIST(310), // process_vm_readv | ||
47 | BLACKLIST(311), // process_vm_writev | ||
48 | BLACKLIST(139), // sysfs | ||
49 | BLACKLIST(156), // _sysctl | ||
50 | BLACKLIST(159), // adjtimex | ||
51 | BLACKLIST(305), // clock_adjtime | ||
52 | BLACKLIST(212), // lookup_dcookie | ||
53 | BLACKLIST(298), // perf_event_open | ||
54 | BLACKLIST(300), // fanotify_init | ||
55 | BLACKLIST(312), // kcmp | ||
56 | BLACKLIST(248), // add_key | ||
57 | BLACKLIST(249), // request_key | ||
58 | BLACKLIST(250), // keyctl | ||
59 | BLACKLIST(134), // uselib | ||
60 | BLACKLIST(163), // acct | ||
61 | BLACKLIST(154), // modify_ldt | ||
62 | BLACKLIST(155), // pivot_root | ||
63 | BLACKLIST(206), // io_setup | ||
64 | BLACKLIST(207), // io_destroy | ||
65 | BLACKLIST(208), // io_getevents | ||
66 | BLACKLIST(209), // io_submit | ||
67 | BLACKLIST(210), // io_cancel | ||
68 | BLACKLIST(216), // remap_file_pages | ||
69 | BLACKLIST(237), // mbind | ||
70 | BLACKLIST(239), // get_mempolicy | ||
71 | BLACKLIST(238), // set_mempolicy | ||
72 | BLACKLIST(256), // migrate_pages | ||
73 | BLACKLIST(279), // move_pages | ||
74 | BLACKLIST(278), // vmsplice | ||
75 | BLACKLIST(161), // chroot | ||
76 | BLACKLIST(184), // tuxcall | ||
77 | BLACKLIST(169), // reboot | ||
78 | BLACKLIST(180), // nfsservctl | ||
79 | BLACKLIST(177), // get_kernel_syms | ||
80 | |||
81 | RETURN_ALLOW | ||
82 | }; | ||
83 | |||
84 | // save filter to file | ||
85 | int dst = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
86 | if (dst < 0) { | ||
87 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
88 | exit(1); | ||
89 | } | ||
90 | |||
91 | int size = (int) sizeof(filter); | ||
92 | int written = 0; | ||
93 | while (written < size) { | ||
94 | int rv = write(dst, (unsigned char *) filter + written, size - written); | ||
95 | if (rv == -1) { | ||
96 | fprintf(stderr, "Error fseccomp: cannot write %s file\n", fname); | ||
97 | exit(1); | ||
98 | } | ||
99 | written += rv; | ||
100 | } | ||
101 | close(dst); | ||
102 | } | ||
103 | |||
104 | // i386 filter installed on amd64 architectures | ||
105 | void seccomp_secondary_32(const char *fname) { | ||
106 | // hardcoded syscall values | ||
107 | struct sock_filter filter[] = { | ||
108 | VALIDATE_ARCHITECTURE_32, | ||
109 | EXAMINE_SYSCALL, | ||
110 | BLACKLIST(21), // mount | ||
111 | BLACKLIST(52), // umount2 | ||
112 | // todo: implement --allow-debuggers | ||
113 | BLACKLIST(26), // ptrace | ||
114 | BLACKLIST(283), // kexec_load | ||
115 | BLACKLIST(341), // name_to_handle_at | ||
116 | BLACKLIST(342), // open_by_handle_at | ||
117 | BLACKLIST(127), // create_module | ||
118 | BLACKLIST(128), // init_module | ||
119 | BLACKLIST(350), // finit_module | ||
120 | BLACKLIST(129), // delete_module | ||
121 | BLACKLIST(110), // iopl | ||
122 | BLACKLIST(101), // ioperm | ||
123 | BLACKLIST(289), // ioprio_set | ||
124 | BLACKLIST(87), // swapon | ||
125 | BLACKLIST(115), // swapoff | ||
126 | BLACKLIST(103), // syslog | ||
127 | BLACKLIST(347), // process_vm_readv | ||
128 | BLACKLIST(348), // process_vm_writev | ||
129 | BLACKLIST(135), // sysfs | ||
130 | BLACKLIST(149), // _sysctl | ||
131 | BLACKLIST(124), // adjtimex | ||
132 | BLACKLIST(343), // clock_adjtime | ||
133 | BLACKLIST(253), // lookup_dcookie | ||
134 | BLACKLIST(336), // perf_event_open | ||
135 | BLACKLIST(338), // fanotify_init | ||
136 | BLACKLIST(349), // kcmp | ||
137 | BLACKLIST(286), // add_key | ||
138 | BLACKLIST(287), // request_key | ||
139 | BLACKLIST(288), // keyctl | ||
140 | BLACKLIST(86), // uselib | ||
141 | BLACKLIST(51), // acct | ||
142 | BLACKLIST(123), // modify_ldt | ||
143 | BLACKLIST(217), // pivot_root | ||
144 | BLACKLIST(245), // io_setup | ||
145 | BLACKLIST(246), // io_destroy | ||
146 | BLACKLIST(247), // io_getevents | ||
147 | BLACKLIST(248), // io_submit | ||
148 | BLACKLIST(249), // io_cancel | ||
149 | BLACKLIST(257), // remap_file_pages | ||
150 | BLACKLIST(274), // mbind | ||
151 | BLACKLIST(275), // get_mempolicy | ||
152 | BLACKLIST(276), // set_mempolicy | ||
153 | BLACKLIST(294), // migrate_pages | ||
154 | BLACKLIST(317), // move_pages | ||
155 | BLACKLIST(316), // vmsplice | ||
156 | BLACKLIST(61), // chroot | ||
157 | BLACKLIST(88), // reboot | ||
158 | BLACKLIST(169), // nfsservctl | ||
159 | BLACKLIST(130), // get_kernel_syms | ||
160 | |||
161 | RETURN_ALLOW | ||
162 | }; | ||
163 | |||
164 | // save filter to file | ||
165 | int dst = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
166 | if (dst < 0) { | ||
167 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
168 | exit(1); | ||
169 | } | ||
170 | |||
171 | int size = (int) sizeof(filter); | ||
172 | int written = 0; | ||
173 | while (written < size) { | ||
174 | int rv = write(dst, (unsigned char *) filter + written, size - written); | ||
175 | if (rv == -1) { | ||
176 | fprintf(stderr, "Error fseccomp: cannot write %s file\n", fname); | ||
177 | exit(1); | ||
178 | } | ||
179 | written += rv; | ||
180 | } | ||
181 | close(dst); | ||
182 | } | ||
183 | |||
diff --git a/src/firejail/syscall.c b/src/fseccomp/syscall.c index 985cc8bb8..7c2c4cbb2 100644 --- a/src/firejail/syscall.c +++ b/src/fseccomp/syscall.c | |||
@@ -17,9 +17,7 @@ | |||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | 17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | 20 | #include "fseccomp.h" | |
21 | #ifdef HAVE_SECCOMP | ||
22 | #include "firejail.h" | ||
23 | #include <sys/syscall.h> | 21 | #include <sys/syscall.h> |
24 | 22 | ||
25 | typedef struct { | 23 | typedef struct { |
@@ -31,13 +29,25 @@ static SyscallEntry syslist[] = { | |||
31 | // | 29 | // |
32 | // code generated using tools/extract-syscall | 30 | // code generated using tools/extract-syscall |
33 | // | 31 | // |
34 | #include "syscall.h" | 32 | #include "../include/syscall.h" |
35 | // | 33 | // |
36 | // end of generated code | 34 | // end of generated code |
37 | // | 35 | // |
38 | }; // end of syslist | 36 | }; // end of syslist |
39 | 37 | ||
40 | const char *syscall_find_nr(int nr) { | 38 | // return -1 if error, or syscall number |
39 | int syscall_find_name(const char *name) { | ||
40 | int i; | ||
41 | int elems = sizeof(syslist) / sizeof(syslist[0]); | ||
42 | for (i = 0; i < elems; i++) { | ||
43 | if (strcmp(name, syslist[i].name) == 0) | ||
44 | return syslist[i].nr; | ||
45 | } | ||
46 | |||
47 | return -1; | ||
48 | } | ||
49 | |||
50 | char *syscall_find_nr(int nr) { | ||
41 | int i; | 51 | int i; |
42 | int elems = sizeof(syslist) / sizeof(syslist[0]); | 52 | int elems = sizeof(syslist) / sizeof(syslist[0]); |
43 | for (i = 0; i < elems; i++) { | 53 | for (i = 0; i < elems; i++) { |
@@ -48,24 +58,61 @@ const char *syscall_find_nr(int nr) { | |||
48 | return "unknown"; | 58 | return "unknown"; |
49 | } | 59 | } |
50 | 60 | ||
51 | // return -1 if error, or syscall number | 61 | void syscall_print(void) { |
52 | static int syscall_find_name(const char *name) { | ||
53 | int i; | 62 | int i; |
54 | int elems = sizeof(syslist) / sizeof(syslist[0]); | 63 | int elems = sizeof(syslist) / sizeof(syslist[0]); |
55 | for (i = 0; i < elems; i++) { | 64 | for (i = 0; i < elems; i++) { |
56 | if (strcmp(name, syslist[i].name) == 0) | 65 | printf("%d\t- %s\n", syslist[i].nr, syslist[i].name); |
57 | return syslist[i].nr; | ||
58 | } | 66 | } |
67 | printf("\n"); | ||
68 | } | ||
69 | |||
70 | // allowed input: | ||
71 | // - syscall | ||
72 | // - syscall(error) | ||
73 | static void syscall_process_name(const char *name, int *syscall_nr, int *error_nr) { | ||
74 | assert(name); | ||
75 | if (strlen(name) == 0) | ||
76 | goto error; | ||
77 | *error_nr = -1; | ||
59 | 78 | ||
60 | return -1; | 79 | // syntax check |
80 | char *str = strdup(name); | ||
81 | if (!str) | ||
82 | errExit("strdup"); | ||
83 | |||
84 | char *syscall_name = str; | ||
85 | char *error_name = strchr(str, ':'); | ||
86 | if (error_name) { | ||
87 | *error_name = '\0'; | ||
88 | error_name++; | ||
89 | } | ||
90 | if (strlen(syscall_name) == 0) { | ||
91 | free(str); | ||
92 | goto error; | ||
93 | } | ||
94 | |||
95 | *syscall_nr = syscall_find_name(syscall_name); | ||
96 | if (error_name) { | ||
97 | *error_nr = errno_find_name(error_name); | ||
98 | if (*error_nr == -1) | ||
99 | *syscall_nr = -1; | ||
100 | } | ||
101 | |||
102 | free(str); | ||
103 | return; | ||
104 | |||
105 | error: | ||
106 | fprintf(stderr, "Error fseccomp: invalid syscall list entry %s\n", name); | ||
107 | exit(1); | ||
61 | } | 108 | } |
62 | 109 | ||
63 | // return 1 if error, 0 if OK | 110 | // return 1 if error, 0 if OK |
64 | int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg), int arg) { | 111 | int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall, int arg), int fd, int arg) { |
65 | // don't allow empty lists | 112 | // don't allow empty lists |
66 | if (slist == NULL || *slist == '\0') { | 113 | if (slist == NULL || *slist == '\0') { |
67 | fprintf(stderr, "Error: empty syscall lists are not allowed\n"); | 114 | fprintf(stderr, "Error fseccomp: empty syscall lists are not allowed\n"); |
68 | return -1; | 115 | exit(1); |
69 | } | 116 | } |
70 | 117 | ||
71 | // work on a copy of the string | 118 | // work on a copy of the string |
@@ -73,44 +120,27 @@ int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg) | |||
73 | if (!str) | 120 | if (!str) |
74 | errExit("strdup"); | 121 | errExit("strdup"); |
75 | 122 | ||
76 | char *ptr = str; | 123 | char *ptr =strtok(str, ","); |
77 | char *start = str; | 124 | if (ptr == NULL) { |
78 | while (*ptr != '\0') { | 125 | fprintf(stderr, "Error fseccomp: empty syscall lists are not allowed\n"); |
79 | if (islower(*ptr) || isdigit(*ptr) || *ptr == '_') | 126 | exit(1); |
80 | ; | ||
81 | else if (*ptr == ',') { | ||
82 | *ptr = '\0'; | ||
83 | int nr = syscall_find_name(start); | ||
84 | if (nr == -1) | ||
85 | fprintf(stderr, "Warning: syscall %s not found\n", start); | ||
86 | else if (callback != NULL) | ||
87 | callback(nr, arg); | ||
88 | |||
89 | start = ptr + 1; | ||
90 | } | ||
91 | ptr++; | ||
92 | } | 127 | } |
93 | if (*start != '\0') { | 128 | |
94 | int nr = syscall_find_name(start); | 129 | while (ptr) { |
95 | if (nr == -1) | 130 | int syscall_nr; |
96 | fprintf(stderr, "Warning: syscall %s not found\n", start); | 131 | int error_nr; |
97 | else if (callback != NULL) | 132 | syscall_process_name(ptr, &syscall_nr, &error_nr); |
98 | callback(nr, arg); | 133 | if (syscall_nr == -1) |
134 | fprintf(stderr, "Warning fseccomp: syscall %s not found\n", ptr); | ||
135 | else if (callback != NULL) { | ||
136 | if (error_nr != -1) | ||
137 | filter_add_errno(fd, syscall_nr, error_nr); | ||
138 | else | ||
139 | callback(fd, syscall_nr, arg); | ||
140 | } | ||
141 | ptr = strtok(NULL, ","); | ||
99 | } | 142 | } |
100 | 143 | ||
101 | free(str); | 144 | free(str); |
102 | return 0; | 145 | return 0; |
103 | } | 146 | } |
104 | |||
105 | void syscall_print(void) { | ||
106 | EUID_ASSERT(); | ||
107 | |||
108 | int i; | ||
109 | int elems = sizeof(syslist) / sizeof(syslist[0]); | ||
110 | for (i = 0; i < elems; i++) { | ||
111 | printf("%d\t- %s\n", syslist[i].nr, syslist[i].name); | ||
112 | } | ||
113 | printf("\n"); | ||
114 | } | ||
115 | |||
116 | #endif // HAVE_SECCOMP | ||
diff --git a/src/ftee/Makefile.in b/src/ftee/Makefile.in index be159225f..ad508cadd 100644 --- a/src/ftee/Makefile.in +++ b/src/ftee/Makefile.in | |||
@@ -4,21 +4,23 @@ PREFIX=@prefix@ | |||
4 | VERSION=@PACKAGE_VERSION@ | 4 | VERSION=@PACKAGE_VERSION@ |
5 | NAME=@PACKAGE_NAME@ | 5 | NAME=@PACKAGE_NAME@ |
6 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ | 6 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ |
7 | HAVE_GCOV=@HAVE_GCOV@ | ||
8 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
7 | 9 | ||
8 | H_FILE_LIST = $(sort $(wildcard *.[h])) | 10 | H_FILE_LIST = $(sort $(wildcard *.[h])) |
9 | C_FILE_LIST = $(sort $(wildcard *.c)) | 11 | C_FILE_LIST = $(sort $(wildcard *.c)) |
10 | OBJS = $(C_FILE_LIST:.c=.o) | 12 | OBJS = $(C_FILE_LIST:.c=.o) |
11 | BINOBJS = $(foreach file, $(OBJS), $file) | 13 | BINOBJS = $(foreach file, $(OBJS), $file) |
12 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(PREFIX)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | 14 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) -DPREFIX='"$(PREFIX)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security |
13 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | 15 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread |
14 | 16 | ||
15 | %.o : %.c $(H_FILE_LIST) | 17 | %.o : %.c $(H_FILE_LIST) |
16 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | 18 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ |
17 | 19 | ||
18 | ftee: $(OBJS) | 20 | ftee: $(OBJS) |
19 | $(CC) $(LDFLAGS) -o $@ $(OBJS) | 21 | $(CC) $(LDFLAGS) -o $@ $(OBJS) $(EXTRA_LDFLAGS) |
20 | 22 | ||
21 | clean:; rm -f *.o ftee | 23 | clean:; rm -f *.o ftee *.gcov *.gcda *.gcno |
22 | 24 | ||
23 | distclean: clean | 25 | distclean: clean |
24 | rm -fr Makefile | 26 | rm -fr Makefile |
diff --git a/src/ftee/main.c b/src/ftee/main.c index 8daea8487..e6aa5f567 100644 --- a/src/ftee/main.c +++ b/src/ftee/main.c | |||
@@ -193,6 +193,10 @@ int main(int argc, char **argv) { | |||
193 | usage(); | 193 | usage(); |
194 | exit(1); | 194 | exit(1); |
195 | } | 195 | } |
196 | if (strcmp(argv[1], "--help") == 0) { | ||
197 | usage(); | ||
198 | return 0; | ||
199 | } | ||
196 | char *fname = argv[1]; | 200 | char *fname = argv[1]; |
197 | 201 | ||
198 | 202 | ||
diff --git a/src/include/common.h b/src/include/common.h index cd4b9c874..108820290 100644 --- a/src/include/common.h +++ b/src/include/common.h | |||
@@ -32,7 +32,7 @@ | |||
32 | #include <ctype.h> | 32 | #include <ctype.h> |
33 | #include <assert.h> | 33 | #include <assert.h> |
34 | 34 | ||
35 | #define errExit(msg) do { char msgout[500]; sprintf(msgout, "Error %s:%s(%d)", msg, __FUNCTION__, __LINE__); perror(msgout); exit(1);} while (0) | 35 | #define errExit(msg) do { char msgout[500]; sprintf(msgout, "Error %s: %s:%d %s", msg, __FILE__, __LINE__, __FUNCTION__); perror(msgout); exit(1);} while (0) |
36 | 36 | ||
37 | // macro to print ip addresses in a printf statement | 37 | // macro to print ip addresses in a printf statement |
38 | #define PRINT_IP(A) \ | 38 | #define PRINT_IP(A) \ |
@@ -113,4 +113,6 @@ int join_namespace(pid_t pid, char *type); | |||
113 | int name2pid(const char *name, pid_t *pid); | 113 | int name2pid(const char *name, pid_t *pid); |
114 | char *pid_proc_comm(const pid_t pid); | 114 | char *pid_proc_comm(const pid_t pid); |
115 | char *pid_proc_cmdline(const pid_t pid); | 115 | char *pid_proc_cmdline(const pid_t pid); |
116 | int pid_proc_cmdline_x11_xpra_xephyr(const pid_t pid); | ||
117 | int pid_hidepid(void); | ||
116 | #endif | 118 | #endif |
diff --git a/src/include/euid_common.h b/src/include/euid_common.h index f07cf2868..752df5fff 100644 --- a/src/include/euid_common.h +++ b/src/include/euid_common.h | |||
@@ -31,25 +31,32 @@ | |||
31 | } | 31 | } |
32 | 32 | ||
33 | extern uid_t firejail_uid; | 33 | extern uid_t firejail_uid; |
34 | extern uid_t firejail_gid; | ||
34 | 35 | ||
35 | 36 | ||
36 | 37 | ||
37 | static inline void EUID_ROOT(void) { | 38 | static inline void EUID_ROOT(void) { |
38 | if (seteuid(0) == -1) | 39 | if (seteuid(0) == -1) |
39 | fprintf(stderr, "Error: cannot switch euid to root\n"); | 40 | fprintf(stderr, "Warning: cannot switch euid to root\n"); |
41 | if (setegid(0) == -1) | ||
42 | fprintf(stderr, "Warning: cannot switch egid to root\n"); | ||
40 | } | 43 | } |
41 | 44 | ||
42 | static inline void EUID_USER(void) { | 45 | static inline void EUID_USER(void) { |
43 | if (seteuid(firejail_uid) == -1) | 46 | if (seteuid(firejail_uid) == -1) |
44 | fprintf(stderr, "Error: cannot switch euid to user\n"); | 47 | errExit("seteuid"); |
48 | if (setegid(firejail_gid) == -1) | ||
49 | errExit("setegid"); | ||
45 | } | 50 | } |
46 | 51 | ||
47 | static inline void EUID_PRINT(void) { | 52 | static inline void EUID_PRINT(void) { |
48 | printf("debug: uid %d, euid %d\n", getuid(), geteuid()); | 53 | printf("debug: uid %d, euid %d\n", getuid(), geteuid()); |
54 | printf("debug: gid %d, egid %d\n", getgid(), getegid()); | ||
49 | } | 55 | } |
50 | 56 | ||
51 | static inline void EUID_INIT(void) { | 57 | static inline void EUID_INIT(void) { |
52 | firejail_uid = getuid(); | 58 | firejail_uid = getuid(); |
59 | firejail_gid = getgid(); | ||
53 | } | 60 | } |
54 | 61 | ||
55 | #endif | 62 | #endif |
diff --git a/src/firejail/seccomp.h b/src/include/seccomp.h index 7d646dd9e..7d646dd9e 100644 --- a/src/firejail/seccomp.h +++ b/src/include/seccomp.h | |||
diff --git a/src/firejail/syscall.h b/src/include/syscall.h index 5b2cb4915..9a29779c9 100644 --- a/src/firejail/syscall.h +++ b/src/include/syscall.h | |||
@@ -20,7 +20,6 @@ | |||
20 | 20 | ||
21 | // content extracted from /bits/syscall.h file form glibc 2.22 | 21 | // content extracted from /bits/syscall.h file form glibc 2.22 |
22 | // using ../tools/extract_syscall tool | 22 | // using ../tools/extract_syscall tool |
23 | |||
24 | #if !defined __x86_64__ | 23 | #if !defined __x86_64__ |
25 | #ifdef SYS__llseek | 24 | #ifdef SYS__llseek |
26 | #ifdef __NR__llseek | 25 | #ifdef __NR__llseek |
@@ -37,6 +36,11 @@ | |||
37 | {"_sysctl", __NR__sysctl}, | 36 | {"_sysctl", __NR__sysctl}, |
38 | #endif | 37 | #endif |
39 | #endif | 38 | #endif |
39 | #ifdef SYS_accept4 | ||
40 | #ifdef __NR_accept4 | ||
41 | {"accept4", __NR_accept4}, | ||
42 | #endif | ||
43 | #endif | ||
40 | #ifdef SYS_access | 44 | #ifdef SYS_access |
41 | #ifdef __NR_access | 45 | #ifdef __NR_access |
42 | {"access", __NR_access}, | 46 | {"access", __NR_access}, |
@@ -72,6 +76,11 @@ | |||
72 | {"bdflush", __NR_bdflush}, | 76 | {"bdflush", __NR_bdflush}, |
73 | #endif | 77 | #endif |
74 | #endif | 78 | #endif |
79 | #ifdef SYS_bind | ||
80 | #ifdef __NR_bind | ||
81 | {"bind", __NR_bind}, | ||
82 | #endif | ||
83 | #endif | ||
75 | #ifdef SYS_bpf | 84 | #ifdef SYS_bpf |
76 | #ifdef __NR_bpf | 85 | #ifdef __NR_bpf |
77 | {"bpf", __NR_bpf}, | 86 | {"bpf", __NR_bpf}, |
@@ -157,6 +166,16 @@ | |||
157 | {"close", __NR_close}, | 166 | {"close", __NR_close}, |
158 | #endif | 167 | #endif |
159 | #endif | 168 | #endif |
169 | #ifdef SYS_connect | ||
170 | #ifdef __NR_connect | ||
171 | {"connect", __NR_connect}, | ||
172 | #endif | ||
173 | #endif | ||
174 | #ifdef SYS_copy_file_range | ||
175 | #ifdef __NR_copy_file_range | ||
176 | {"copy_file_range", __NR_copy_file_range}, | ||
177 | #endif | ||
178 | #endif | ||
160 | #ifdef SYS_creat | 179 | #ifdef SYS_creat |
161 | #ifdef __NR_creat | 180 | #ifdef __NR_creat |
162 | {"creat", __NR_creat}, | 181 | {"creat", __NR_creat}, |
@@ -492,6 +511,11 @@ | |||
492 | {"getitimer", __NR_getitimer}, | 511 | {"getitimer", __NR_getitimer}, |
493 | #endif | 512 | #endif |
494 | #endif | 513 | #endif |
514 | #ifdef SYS_getpeername | ||
515 | #ifdef __NR_getpeername | ||
516 | {"getpeername", __NR_getpeername}, | ||
517 | #endif | ||
518 | #endif | ||
495 | #ifdef SYS_getpgid | 519 | #ifdef SYS_getpgid |
496 | #ifdef __NR_getpgid | 520 | #ifdef __NR_getpgid |
497 | {"getpgid", __NR_getpgid}, | 521 | {"getpgid", __NR_getpgid}, |
@@ -562,6 +586,16 @@ | |||
562 | {"getsid", __NR_getsid}, | 586 | {"getsid", __NR_getsid}, |
563 | #endif | 587 | #endif |
564 | #endif | 588 | #endif |
589 | #ifdef SYS_getsockname | ||
590 | #ifdef __NR_getsockname | ||
591 | {"getsockname", __NR_getsockname}, | ||
592 | #endif | ||
593 | #endif | ||
594 | #ifdef SYS_getsockopt | ||
595 | #ifdef __NR_getsockopt | ||
596 | {"getsockopt", __NR_getsockopt}, | ||
597 | #endif | ||
598 | #endif | ||
565 | #ifdef SYS_gettid | 599 | #ifdef SYS_gettid |
566 | #ifdef __NR_gettid | 600 | #ifdef __NR_gettid |
567 | {"gettid", __NR_gettid}, | 601 | {"gettid", __NR_gettid}, |
@@ -722,6 +756,11 @@ | |||
722 | {"linkat", __NR_linkat}, | 756 | {"linkat", __NR_linkat}, |
723 | #endif | 757 | #endif |
724 | #endif | 758 | #endif |
759 | #ifdef SYS_listen | ||
760 | #ifdef __NR_listen | ||
761 | {"listen", __NR_listen}, | ||
762 | #endif | ||
763 | #endif | ||
725 | #ifdef SYS_listxattr | 764 | #ifdef SYS_listxattr |
726 | #ifdef __NR_listxattr | 765 | #ifdef __NR_listxattr |
727 | {"listxattr", __NR_listxattr}, | 766 | {"listxattr", __NR_listxattr}, |
@@ -777,6 +816,11 @@ | |||
777 | {"mbind", __NR_mbind}, | 816 | {"mbind", __NR_mbind}, |
778 | #endif | 817 | #endif |
779 | #endif | 818 | #endif |
819 | #ifdef SYS_membarrier | ||
820 | #ifdef __NR_membarrier | ||
821 | {"membarrier", __NR_membarrier}, | ||
822 | #endif | ||
823 | #endif | ||
780 | #ifdef SYS_memfd_create | 824 | #ifdef SYS_memfd_create |
781 | #ifdef __NR_memfd_create | 825 | #ifdef __NR_memfd_create |
782 | {"memfd_create", __NR_memfd_create}, | 826 | {"memfd_create", __NR_memfd_create}, |
@@ -817,6 +861,11 @@ | |||
817 | {"mlock", __NR_mlock}, | 861 | {"mlock", __NR_mlock}, |
818 | #endif | 862 | #endif |
819 | #endif | 863 | #endif |
864 | #ifdef SYS_mlock2 | ||
865 | #ifdef __NR_mlock2 | ||
866 | {"mlock2", __NR_mlock2}, | ||
867 | #endif | ||
868 | #endif | ||
820 | #ifdef SYS_mlockall | 869 | #ifdef SYS_mlockall |
821 | #ifdef __NR_mlockall | 870 | #ifdef __NR_mlockall |
822 | {"mlockall", __NR_mlockall}, | 871 | {"mlockall", __NR_mlockall}, |
@@ -1122,11 +1171,21 @@ | |||
1122 | {"reboot", __NR_reboot}, | 1171 | {"reboot", __NR_reboot}, |
1123 | #endif | 1172 | #endif |
1124 | #endif | 1173 | #endif |
1174 | #ifdef SYS_recvfrom | ||
1175 | #ifdef __NR_recvfrom | ||
1176 | {"recvfrom", __NR_recvfrom}, | ||
1177 | #endif | ||
1178 | #endif | ||
1125 | #ifdef SYS_recvmmsg | 1179 | #ifdef SYS_recvmmsg |
1126 | #ifdef __NR_recvmmsg | 1180 | #ifdef __NR_recvmmsg |
1127 | {"recvmmsg", __NR_recvmmsg}, | 1181 | {"recvmmsg", __NR_recvmmsg}, |
1128 | #endif | 1182 | #endif |
1129 | #endif | 1183 | #endif |
1184 | #ifdef SYS_recvmsg | ||
1185 | #ifdef __NR_recvmsg | ||
1186 | {"recvmsg", __NR_recvmsg}, | ||
1187 | #endif | ||
1188 | #endif | ||
1130 | #ifdef SYS_remap_file_pages | 1189 | #ifdef SYS_remap_file_pages |
1131 | #ifdef __NR_remap_file_pages | 1190 | #ifdef __NR_remap_file_pages |
1132 | {"remap_file_pages", __NR_remap_file_pages}, | 1191 | {"remap_file_pages", __NR_remap_file_pages}, |
@@ -1292,6 +1351,16 @@ | |||
1292 | {"sendmmsg", __NR_sendmmsg}, | 1351 | {"sendmmsg", __NR_sendmmsg}, |
1293 | #endif | 1352 | #endif |
1294 | #endif | 1353 | #endif |
1354 | #ifdef SYS_sendmsg | ||
1355 | #ifdef __NR_sendmsg | ||
1356 | {"sendmsg", __NR_sendmsg}, | ||
1357 | #endif | ||
1358 | #endif | ||
1359 | #ifdef SYS_sendto | ||
1360 | #ifdef __NR_sendto | ||
1361 | {"sendto", __NR_sendto}, | ||
1362 | #endif | ||
1363 | #endif | ||
1295 | #ifdef SYS_set_mempolicy | 1364 | #ifdef SYS_set_mempolicy |
1296 | #ifdef __NR_set_mempolicy | 1365 | #ifdef __NR_set_mempolicy |
1297 | {"set_mempolicy", __NR_set_mempolicy}, | 1366 | {"set_mempolicy", __NR_set_mempolicy}, |
@@ -1432,6 +1501,11 @@ | |||
1432 | {"setsid", __NR_setsid}, | 1501 | {"setsid", __NR_setsid}, |
1433 | #endif | 1502 | #endif |
1434 | #endif | 1503 | #endif |
1504 | #ifdef SYS_setsockopt | ||
1505 | #ifdef __NR_setsockopt | ||
1506 | {"setsockopt", __NR_setsockopt}, | ||
1507 | #endif | ||
1508 | #endif | ||
1435 | #ifdef SYS_settimeofday | 1509 | #ifdef SYS_settimeofday |
1436 | #ifdef __NR_settimeofday | 1510 | #ifdef __NR_settimeofday |
1437 | {"settimeofday", __NR_settimeofday}, | 1511 | {"settimeofday", __NR_settimeofday}, |
@@ -1457,6 +1531,11 @@ | |||
1457 | {"sgetmask", __NR_sgetmask}, | 1531 | {"sgetmask", __NR_sgetmask}, |
1458 | #endif | 1532 | #endif |
1459 | #endif | 1533 | #endif |
1534 | #ifdef SYS_shutdown | ||
1535 | #ifdef __NR_shutdown | ||
1536 | {"shutdown", __NR_shutdown}, | ||
1537 | #endif | ||
1538 | #endif | ||
1460 | #ifdef SYS_sigaction | 1539 | #ifdef SYS_sigaction |
1461 | #ifdef __NR_sigaction | 1540 | #ifdef __NR_sigaction |
1462 | {"sigaction", __NR_sigaction}, | 1541 | {"sigaction", __NR_sigaction}, |
@@ -1502,11 +1581,21 @@ | |||
1502 | {"sigsuspend", __NR_sigsuspend}, | 1581 | {"sigsuspend", __NR_sigsuspend}, |
1503 | #endif | 1582 | #endif |
1504 | #endif | 1583 | #endif |
1584 | #ifdef SYS_socket | ||
1585 | #ifdef __NR_socket | ||
1586 | {"socket", __NR_socket}, | ||
1587 | #endif | ||
1588 | #endif | ||
1505 | #ifdef SYS_socketcall | 1589 | #ifdef SYS_socketcall |
1506 | #ifdef __NR_socketcall | 1590 | #ifdef __NR_socketcall |
1507 | {"socketcall", __NR_socketcall}, | 1591 | {"socketcall", __NR_socketcall}, |
1508 | #endif | 1592 | #endif |
1509 | #endif | 1593 | #endif |
1594 | #ifdef SYS_socketpair | ||
1595 | #ifdef __NR_socketpair | ||
1596 | {"socketpair", __NR_socketpair}, | ||
1597 | #endif | ||
1598 | #endif | ||
1510 | #ifdef SYS_splice | 1599 | #ifdef SYS_splice |
1511 | #ifdef __NR_splice | 1600 | #ifdef __NR_splice |
1512 | {"splice", __NR_splice}, | 1601 | {"splice", __NR_splice}, |
@@ -1722,6 +1811,11 @@ | |||
1722 | {"uselib", __NR_uselib}, | 1811 | {"uselib", __NR_uselib}, |
1723 | #endif | 1812 | #endif |
1724 | #endif | 1813 | #endif |
1814 | #ifdef SYS_userfaultfd | ||
1815 | #ifdef __NR_userfaultfd | ||
1816 | {"userfaultfd", __NR_userfaultfd}, | ||
1817 | #endif | ||
1818 | #endif | ||
1725 | #ifdef SYS_ustat | 1819 | #ifdef SYS_ustat |
1726 | #ifdef __NR_ustat | 1820 | #ifdef __NR_ustat |
1727 | {"ustat", __NR_ustat}, | 1821 | {"ustat", __NR_ustat}, |
@@ -1934,6 +2028,11 @@ | |||
1934 | {"connect", __NR_connect}, | 2028 | {"connect", __NR_connect}, |
1935 | #endif | 2029 | #endif |
1936 | #endif | 2030 | #endif |
2031 | #ifdef SYS_copy_file_range | ||
2032 | #ifdef __NR_copy_file_range | ||
2033 | {"copy_file_range", __NR_copy_file_range}, | ||
2034 | #endif | ||
2035 | #endif | ||
1937 | #ifdef SYS_creat | 2036 | #ifdef SYS_creat |
1938 | #ifdef __NR_creat | 2037 | #ifdef __NR_creat |
1939 | {"creat", __NR_creat}, | 2038 | {"creat", __NR_creat}, |
@@ -2484,6 +2583,11 @@ | |||
2484 | {"mbind", __NR_mbind}, | 2583 | {"mbind", __NR_mbind}, |
2485 | #endif | 2584 | #endif |
2486 | #endif | 2585 | #endif |
2586 | #ifdef SYS_membarrier | ||
2587 | #ifdef __NR_membarrier | ||
2588 | {"membarrier", __NR_membarrier}, | ||
2589 | #endif | ||
2590 | #endif | ||
2487 | #ifdef SYS_memfd_create | 2591 | #ifdef SYS_memfd_create |
2488 | #ifdef __NR_memfd_create | 2592 | #ifdef __NR_memfd_create |
2489 | {"memfd_create", __NR_memfd_create}, | 2593 | {"memfd_create", __NR_memfd_create}, |
@@ -2524,6 +2628,11 @@ | |||
2524 | {"mlock", __NR_mlock}, | 2628 | {"mlock", __NR_mlock}, |
2525 | #endif | 2629 | #endif |
2526 | #endif | 2630 | #endif |
2631 | #ifdef SYS_mlock2 | ||
2632 | #ifdef __NR_mlock2 | ||
2633 | {"mlock2", __NR_mlock2}, | ||
2634 | #endif | ||
2635 | #endif | ||
2527 | #ifdef SYS_mlockall | 2636 | #ifdef SYS_mlockall |
2528 | #ifdef __NR_mlockall | 2637 | #ifdef __NR_mlockall |
2529 | {"mlockall", __NR_mlockall}, | 2638 | {"mlockall", __NR_mlockall}, |
@@ -3354,6 +3463,11 @@ | |||
3354 | {"uselib", __NR_uselib}, | 3463 | {"uselib", __NR_uselib}, |
3355 | #endif | 3464 | #endif |
3356 | #endif | 3465 | #endif |
3466 | #ifdef SYS_userfaultfd | ||
3467 | #ifdef __NR_userfaultfd | ||
3468 | {"userfaultfd", __NR_userfaultfd}, | ||
3469 | #endif | ||
3470 | #endif | ||
3357 | #ifdef SYS_ustat | 3471 | #ifdef SYS_ustat |
3358 | #ifdef __NR_ustat | 3472 | #ifdef __NR_ustat |
3359 | {"ustat", __NR_ustat}, | 3473 | {"ustat", __NR_ustat}, |
@@ -3546,6 +3660,11 @@ | |||
3546 | {"connect", __NR_connect}, | 3660 | {"connect", __NR_connect}, |
3547 | #endif | 3661 | #endif |
3548 | #endif | 3662 | #endif |
3663 | #ifdef SYS_copy_file_range | ||
3664 | #ifdef __NR_copy_file_range | ||
3665 | {"copy_file_range", __NR_copy_file_range}, | ||
3666 | #endif | ||
3667 | #endif | ||
3549 | #ifdef SYS_creat | 3668 | #ifdef SYS_creat |
3550 | #ifdef __NR_creat | 3669 | #ifdef __NR_creat |
3551 | {"creat", __NR_creat}, | 3670 | {"creat", __NR_creat}, |
@@ -4071,6 +4190,11 @@ | |||
4071 | {"mbind", __NR_mbind}, | 4190 | {"mbind", __NR_mbind}, |
4072 | #endif | 4191 | #endif |
4073 | #endif | 4192 | #endif |
4193 | #ifdef SYS_membarrier | ||
4194 | #ifdef __NR_membarrier | ||
4195 | {"membarrier", __NR_membarrier}, | ||
4196 | #endif | ||
4197 | #endif | ||
4074 | #ifdef SYS_memfd_create | 4198 | #ifdef SYS_memfd_create |
4075 | #ifdef __NR_memfd_create | 4199 | #ifdef __NR_memfd_create |
4076 | {"memfd_create", __NR_memfd_create}, | 4200 | {"memfd_create", __NR_memfd_create}, |
@@ -4111,6 +4235,11 @@ | |||
4111 | {"mlock", __NR_mlock}, | 4235 | {"mlock", __NR_mlock}, |
4112 | #endif | 4236 | #endif |
4113 | #endif | 4237 | #endif |
4238 | #ifdef SYS_mlock2 | ||
4239 | #ifdef __NR_mlock2 | ||
4240 | {"mlock2", __NR_mlock2}, | ||
4241 | #endif | ||
4242 | #endif | ||
4114 | #ifdef SYS_mlockall | 4243 | #ifdef SYS_mlockall |
4115 | #ifdef __NR_mlockall | 4244 | #ifdef __NR_mlockall |
4116 | {"mlockall", __NR_mlockall}, | 4245 | {"mlockall", __NR_mlockall}, |
@@ -4921,6 +5050,11 @@ | |||
4921 | {"unshare", __NR_unshare}, | 5050 | {"unshare", __NR_unshare}, |
4922 | #endif | 5051 | #endif |
4923 | #endif | 5052 | #endif |
5053 | #ifdef SYS_userfaultfd | ||
5054 | #ifdef __NR_userfaultfd | ||
5055 | {"userfaultfd", __NR_userfaultfd}, | ||
5056 | #endif | ||
5057 | #endif | ||
4924 | #ifdef SYS_ustat | 5058 | #ifdef SYS_ustat |
4925 | #ifdef __NR_ustat | 5059 | #ifdef __NR_ustat |
4926 | {"ustat", __NR_ustat}, | 5060 | {"ustat", __NR_ustat}, |
diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in index 71f96bab1..5549aca11 100644 --- a/src/lib/Makefile.in +++ b/src/lib/Makefile.in | |||
@@ -2,12 +2,14 @@ PREFIX=@prefix@ | |||
2 | VERSION=@PACKAGE_VERSION@ | 2 | VERSION=@PACKAGE_VERSION@ |
3 | NAME=@PACKAGE_NAME@ | 3 | NAME=@PACKAGE_NAME@ |
4 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ | 4 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ |
5 | HAVE_GCOV=@HAVE_GCOV@ | ||
6 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
5 | 7 | ||
6 | H_FILE_LIST = $(sort $(wildcard *.[h])) | 8 | H_FILE_LIST = $(sort $(wildcard *.[h])) |
7 | C_FILE_LIST = $(sort $(wildcard *.c)) | 9 | C_FILE_LIST = $(sort $(wildcard *.c)) |
8 | OBJS = $(C_FILE_LIST:.c=.o) | 10 | OBJS = $(C_FILE_LIST:.c=.o) |
9 | BINOBJS = $(foreach file, $(OBJS), $file) | 11 | BINOBJS = $(foreach file, $(OBJS), $file) |
10 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIC -Wformat -Wformat-security | 12 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIC -Wformat -Wformat-security |
11 | LDFLAGS:=-pic -Wl,-z,relro -Wl,-z,now | 13 | LDFLAGS:=-pic -Wl,-z,relro -Wl,-z,now |
12 | 14 | ||
13 | all: $(OBJS) | 15 | all: $(OBJS) |
@@ -15,7 +17,7 @@ all: $(OBJS) | |||
15 | %.o : %.c $(H_FILE_LIST) | 17 | %.o : %.c $(H_FILE_LIST) |
16 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | 18 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ |
17 | 19 | ||
18 | clean:; rm -f $(OBJS) | 20 | clean:; rm -f $(OBJS) *.gcov *.gcda *.gcno |
19 | 21 | ||
20 | distclean: clean | 22 | distclean: clean |
21 | rm -fr Makefile | 23 | rm -fr Makefile |
diff --git a/src/lib/common.c b/src/lib/common.c index 8ea926df1..2f2340963 100644 --- a/src/lib/common.c +++ b/src/lib/common.c | |||
@@ -199,3 +199,88 @@ char *pid_proc_cmdline(const pid_t pid) { | |||
199 | } | 199 | } |
200 | return rv; | 200 | return rv; |
201 | } | 201 | } |
202 | |||
203 | // return 1 if firejail --x11 on command line | ||
204 | int pid_proc_cmdline_x11_xpra_xephyr(const pid_t pid) { | ||
205 | // if comm is not firejail return 0 | ||
206 | char *comm = pid_proc_comm(pid); | ||
207 | if (strcmp(comm, "firejail") != 0) { | ||
208 | free(comm); | ||
209 | return 0; | ||
210 | } | ||
211 | free(comm); | ||
212 | |||
213 | // open /proc/pid/cmdline file | ||
214 | char *fname; | ||
215 | int fd; | ||
216 | if (asprintf(&fname, "/proc/%d/cmdline", pid) == -1) | ||
217 | return 0; | ||
218 | if ((fd = open(fname, O_RDONLY)) < 0) { | ||
219 | free(fname); | ||
220 | return 0; | ||
221 | } | ||
222 | free(fname); | ||
223 | |||
224 | // read file | ||
225 | unsigned char buffer[BUFLEN]; | ||
226 | ssize_t len; | ||
227 | if ((len = read(fd, buffer, sizeof(buffer) - 1)) <= 0) { | ||
228 | close(fd); | ||
229 | return 0; | ||
230 | } | ||
231 | buffer[len] = '\0'; | ||
232 | close(fd); | ||
233 | |||
234 | // skip the first argument | ||
235 | int i; | ||
236 | for (i = 0; buffer[i] != '\0'; i++); | ||
237 | |||
238 | // parse remaining command line options | ||
239 | while (1) { | ||
240 | // extract argument | ||
241 | i++; | ||
242 | if (i >= len) | ||
243 | break; | ||
244 | char *arg = (char *)buffer + i; | ||
245 | |||
246 | // detect the last command line option | ||
247 | if (strcmp(arg, "--") == 0) | ||
248 | break; | ||
249 | if (strncmp(arg, "--", 2) != 0) | ||
250 | break; | ||
251 | |||
252 | if (strcmp(arg, "--x11=xorg") == 0) | ||
253 | return 0; | ||
254 | |||
255 | // check x11 xpra or xephyr | ||
256 | if (strncmp(arg, "--x11", 5) == 0) | ||
257 | return 1; | ||
258 | i += strlen(arg); | ||
259 | } | ||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | // return 1 if /proc is mounted hidepid, or if /proc/mouns access is denied | ||
264 | #define BUFLEN 4096 | ||
265 | int pid_hidepid(void) { | ||
266 | FILE *fp = fopen("/proc/mounts", "r"); | ||
267 | if (!fp) | ||
268 | return 1; | ||
269 | |||
270 | char buf[BUFLEN]; | ||
271 | while (fgets(buf, BUFLEN, fp)) { | ||
272 | if (strstr(buf, "proc /proc proc")) { | ||
273 | fclose(fp); | ||
274 | // check hidepid | ||
275 | if (strstr(buf, "hidepid=2") || strstr(buf, "hidepid=1")) | ||
276 | return 1; | ||
277 | return 0; | ||
278 | } | ||
279 | } | ||
280 | |||
281 | fclose(fp); | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | |||
286 | |||
diff --git a/src/lib/libnetlink.c b/src/lib/libnetlink.c index 07457eefe..836cf417d 100644 --- a/src/lib/libnetlink.c +++ b/src/lib/libnetlink.c | |||
@@ -723,7 +723,7 @@ int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) | |||
723 | int len = RTA_LENGTH(4); | 723 | int len = RTA_LENGTH(4); |
724 | struct rtattr *subrta; | 724 | struct rtattr *subrta; |
725 | 725 | ||
726 | if (RTA_ALIGN(rta->rta_len) + len > maxlen) { | 726 | if ((int) (RTA_ALIGN(rta->rta_len) + len) > maxlen) { |
727 | fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen); | 727 | fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen); |
728 | return -1; | 728 | return -1; |
729 | } | 729 | } |
@@ -741,7 +741,7 @@ int rta_addattr_l(struct rtattr *rta, int maxlen, int type, | |||
741 | struct rtattr *subrta; | 741 | struct rtattr *subrta; |
742 | int len = RTA_LENGTH(alen); | 742 | int len = RTA_LENGTH(alen); |
743 | 743 | ||
744 | if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) { | 744 | if ((int) (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len)) > maxlen) { |
745 | fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen); | 745 | fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen); |
746 | return -1; | 746 | return -1; |
747 | } | 747 | } |
diff --git a/src/lib/pid.c b/src/lib/pid.c index d1ade389e..ed583c51d 100644 --- a/src/lib/pid.c +++ b/src/lib/pid.c | |||
@@ -29,7 +29,6 @@ | |||
29 | //Process pids[max_pids]; | 29 | //Process pids[max_pids]; |
30 | Process *pids = NULL; | 30 | Process *pids = NULL; |
31 | int max_pids=32769; | 31 | int max_pids=32769; |
32 | #define PIDS_BUFLEN 4096 | ||
33 | 32 | ||
34 | // get the memory associated with this pid | 33 | // get the memory associated with this pid |
35 | void pid_getmem(unsigned pid, unsigned *rss, unsigned *shared) { | 34 | void pid_getmem(unsigned pid, unsigned *rss, unsigned *shared) { |
@@ -340,18 +339,12 @@ void pid_read(pid_t mon_pid) { | |||
340 | exit(1); | 339 | exit(1); |
341 | } | 340 | } |
342 | 341 | ||
343 | if (mon_pid == 0 && strncmp(ptr, "firejail", 8) == 0) { | 342 | if ((strncmp(ptr, "firejail", 8) == 0) && (mon_pid == 0 || mon_pid == pid)) { |
344 | pids[pid].level = 1; | 343 | if (pid_proc_cmdline_x11_xpra_xephyr(pid)) |
345 | } | 344 | pids[pid].level = -1; |
346 | else if (mon_pid == pid && strncmp(ptr, "firejail", 8) == 0) { | 345 | else |
347 | pids[pid].level = 1; | 346 | pids[pid].level = 1; |
348 | } | 347 | } |
349 | // else if (mon_pid == 0 && strncmp(ptr, "lxc-execute", 11) == 0) { | ||
350 | // pids[pid].level = 1; | ||
351 | // } | ||
352 | // else if (mon_pid == pid && strncmp(ptr, "lxc-execute", 11) == 0) { | ||
353 | // pids[pid].level = 1; | ||
354 | // } | ||
355 | else | 348 | else |
356 | pids[pid].level = -1; | 349 | pids[pid].level = -1; |
357 | } | 350 | } |
diff --git a/src/libconnect/Makefile.in b/src/libconnect/Makefile.in new file mode 100644 index 000000000..5b7a8d0f1 --- /dev/null +++ b/src/libconnect/Makefile.in | |||
@@ -0,0 +1,25 @@ | |||
1 | PREFIX=@prefix@ | ||
2 | VERSION=@PACKAGE_VERSION@ | ||
3 | NAME=@PACKAGE_NAME@ | ||
4 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ | ||
5 | |||
6 | H_FILE_LIST = $(sort $(wildcard *.[h])) | ||
7 | C_FILE_LIST = $(sort $(wildcard *.c)) | ||
8 | OBJS = $(C_FILE_LIST:.c=.o) | ||
9 | BINOBJS = $(foreach file, $(OBJS), $file) | ||
10 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIC -Wformat -Wformat-security | ||
11 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now | ||
12 | |||
13 | all: libconnect.so | ||
14 | |||
15 | %.o : %.c $(H_FILE_LIST) | ||
16 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | ||
17 | |||
18 | libconnect.so: $(OBJS) | ||
19 | $(CC) $(LDFLAGS) -shared -fPIC -z relro -o $@ $(OBJS) -ldl | ||
20 | |||
21 | |||
22 | clean:; rm -f $(OBJS) libconnect.so | ||
23 | |||
24 | distclean: clean | ||
25 | rm -fr Makefile | ||
diff --git a/src/libconnect/libconnect.c b/src/libconnect/libconnect.c new file mode 100644 index 000000000..18c4d81f5 --- /dev/null +++ b/src/libconnect/libconnect.c | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | #define _GNU_SOURCE | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <dlfcn.h> | ||
25 | #include <sys/types.h> | ||
26 | #include <unistd.h> | ||
27 | #include <sys/socket.h> | ||
28 | #include <netinet/in.h> | ||
29 | #include <arpa/inet.h> | ||
30 | #include <sys/un.h> | ||
31 | #include <sys/stat.h> | ||
32 | #include <dirent.h> | ||
33 | #include <errno.h> | ||
34 | |||
35 | //#define DEBUG | ||
36 | |||
37 | //static int check_sockaddr(int sockfd, const char *call, const struct sockaddr *addr, int rv) { | ||
38 | static int check_sockaddr(const struct sockaddr *addr) { | ||
39 | if (addr->sa_family == AF_UNIX) { | ||
40 | struct sockaddr_un *a = (struct sockaddr_un *) addr; | ||
41 | if (a->sun_path[0] == '\0' && strstr(a->sun_path + 1, "X11-unix")) { | ||
42 | // printf("@%s\n", a->sun_path + 1); | ||
43 | errno = ENOENT; | ||
44 | return -1; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | // | ||
52 | // syscalls | ||
53 | // | ||
54 | |||
55 | // connect | ||
56 | typedef int (*orig_connect_t)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); | ||
57 | static orig_connect_t orig_connect = NULL; | ||
58 | int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { | ||
59 | if (!orig_connect) | ||
60 | orig_connect = (orig_connect_t)dlsym(RTLD_NEXT, "connect"); | ||
61 | |||
62 | if (check_sockaddr(addr) == -1) | ||
63 | return -1; | ||
64 | |||
65 | return orig_connect(sockfd, addr, addrlen); | ||
66 | } | ||
diff --git a/src/libtrace/libtrace.c b/src/libtrace/libtrace.c index a3d1571f7..dde3df2ea 100644 --- a/src/libtrace/libtrace.c +++ b/src/libtrace/libtrace.c | |||
@@ -423,11 +423,36 @@ int stat(const char *pathname, struct stat *buf) { | |||
423 | typedef int (*orig_stat64_t)(const char *pathname, struct stat64 *buf); | 423 | typedef int (*orig_stat64_t)(const char *pathname, struct stat64 *buf); |
424 | static orig_stat64_t orig_stat64 = NULL; | 424 | static orig_stat64_t orig_stat64 = NULL; |
425 | int stat64(const char *pathname, struct stat64 *buf) { | 425 | int stat64(const char *pathname, struct stat64 *buf) { |
426 | if (!orig_stat) | 426 | if (!orig_stat64) |
427 | orig_stat64 = (orig_stat64_t)dlsym(RTLD_NEXT, "stat64"); | 427 | orig_stat64 = (orig_stat64_t)dlsym(RTLD_NEXT, "stat64"); |
428 | 428 | ||
429 | int rv = orig_stat64(pathname, buf); | 429 | int rv = orig_stat64(pathname, buf); |
430 | printf("%u:%s:stat %s:%d\n", pid(), name(), pathname, rv); | 430 | printf("%u:%s:stat64 %s:%d\n", pid(), name(), pathname, rv); |
431 | return rv; | ||
432 | } | ||
433 | #endif /* __GLIBC__ */ | ||
434 | |||
435 | // lstat | ||
436 | typedef int (*orig_lstat_t)(const char *pathname, struct stat *buf); | ||
437 | static orig_lstat_t orig_lstat = NULL; | ||
438 | int lstat(const char *pathname, struct stat *buf) { | ||
439 | if (!orig_lstat) | ||
440 | orig_lstat = (orig_lstat_t)dlsym(RTLD_NEXT, "lstat"); | ||
441 | |||
442 | int rv = orig_lstat(pathname, buf); | ||
443 | printf("%u:%s:lstat %s:%d\n", pid(), name(), pathname, rv); | ||
444 | return rv; | ||
445 | } | ||
446 | |||
447 | #ifdef __GLIBC__ | ||
448 | typedef int (*orig_lstat64_t)(const char *pathname, struct stat64 *buf); | ||
449 | static orig_lstat64_t orig_lstat64 = NULL; | ||
450 | int lstat64(const char *pathname, struct stat64 *buf) { | ||
451 | if (!orig_lstat64) | ||
452 | orig_lstat64 = (orig_lstat64_t)dlsym(RTLD_NEXT, "lstat64"); | ||
453 | |||
454 | int rv = orig_lstat64(pathname, buf); | ||
455 | printf("%u:%s:lstat64 %s:%d\n", pid(), name(), pathname, rv); | ||
431 | return rv; | 456 | return rv; |
432 | } | 457 | } |
433 | #endif /* __GLIBC__ */ | 458 | #endif /* __GLIBC__ */ |
diff --git a/src/libtracelog/libtracelog.c b/src/libtracelog/libtracelog.c index c3fd40a67..ff884c7d7 100644 --- a/src/libtracelog/libtracelog.c +++ b/src/libtracelog/libtracelog.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <sys/stat.h> | 31 | #include <sys/stat.h> |
32 | #include <syslog.h> | 32 | #include <syslog.h> |
33 | #include <dirent.h> | 33 | #include <dirent.h> |
34 | #include <limits.h> | ||
34 | 35 | ||
35 | //#define DEBUG | 36 | //#define DEBUG |
36 | 37 | ||
@@ -91,9 +92,9 @@ static void storage_add(const char *str) { | |||
91 | storage[h] = ptr; | 92 | storage[h] = ptr; |
92 | } | 93 | } |
93 | 94 | ||
94 | char* cwd = NULL; // global variable for keeping current working directory | 95 | // global variable to keep current working directory |
95 | typedef int (*orig_chdir_t)(const char *pathname); | 96 | static char* cwd = NULL; |
96 | static orig_chdir_t orig_chdir = NULL; | 97 | |
97 | static char *storage_find(const char *str) { | 98 | static char *storage_find(const char *str) { |
98 | #ifdef DEBUG | 99 | #ifdef DEBUG |
99 | printf("storage find %s\n", str); | 100 | printf("storage find %s\n", str); |
@@ -107,17 +108,23 @@ static char *storage_find(const char *str) { | |||
107 | const char *tofind = str; | 108 | const char *tofind = str; |
108 | int allocated = 0; | 109 | int allocated = 0; |
109 | 110 | ||
110 | if (strstr(str, "..") || strstr(str, "/./") || strstr(str, "//") || str[0]!='/') { | 111 | if (strstr(str, "..") || strstr(str, "/./") || strstr(str, "//") || str[0] != '/') { |
111 | if (!orig_chdir) | 112 | if (cwd != NULL && str[0] != '/') { |
112 | orig_chdir = (orig_chdir_t)dlsym(RTLD_NEXT, "chdir"); | 113 | char *fullpath=malloc(PATH_MAX); |
113 | if (!orig_chdir(cwd)) { | 114 | if (!fullpath) { |
114 | #ifdef DEBUG | 115 | fprintf(stderr, "Error: cannot allocate memory\n"); |
115 | printf("chdir failed\n"); | 116 | return NULL; |
116 | #endif | 117 | } |
117 | return NULL; | 118 | if (snprintf(fullpath, PATH_MAX, "%s/%s", cwd, str)<3) { |
119 | fprintf(stderr, "Error: snprintf failed\n"); | ||
120 | free(fullpath); | ||
121 | return NULL; | ||
122 | } | ||
123 | tofind = realpath(fullpath, NULL); | ||
124 | free(fullpath); | ||
125 | } else { | ||
126 | tofind = realpath(str, NULL); | ||
118 | } | 127 | } |
119 | |||
120 | tofind = realpath(str, NULL); | ||
121 | if (!tofind) { | 128 | if (!tofind) { |
122 | #ifdef DEBUG | 129 | #ifdef DEBUG |
123 | printf("realpath failed\n"); | 130 | printf("realpath failed\n"); |
@@ -556,7 +563,7 @@ int stat64(const char *pathname, struct stat64 *buf) { | |||
556 | #ifdef DEBUG | 563 | #ifdef DEBUG |
557 | printf("%s %s\n", __FUNCTION__, pathname); | 564 | printf("%s %s\n", __FUNCTION__, pathname); |
558 | #endif | 565 | #endif |
559 | if (!orig_stat) | 566 | if (!orig_stat64) |
560 | orig_stat64 = (orig_stat64_t)dlsym(RTLD_NEXT, "stat64"); | 567 | orig_stat64 = (orig_stat64_t)dlsym(RTLD_NEXT, "stat64"); |
561 | if (!blacklist_loaded) | 568 | if (!blacklist_loaded) |
562 | load_blacklist(); | 569 | load_blacklist(); |
@@ -592,7 +599,7 @@ int lstat64(const char *pathname, struct stat64 *buf) { | |||
592 | #ifdef DEBUG | 599 | #ifdef DEBUG |
593 | printf("%s %s\n", __FUNCTION__, pathname); | 600 | printf("%s %s\n", __FUNCTION__, pathname); |
594 | #endif | 601 | #endif |
595 | if (!orig_lstat) | 602 | if (!orig_lstat64) |
596 | orig_lstat64 = (orig_lstat64_t)dlsym(RTLD_NEXT, "lstat64"); | 603 | orig_lstat64 = (orig_lstat64_t)dlsym(RTLD_NEXT, "lstat64"); |
597 | if (!blacklist_loaded) | 604 | if (!blacklist_loaded) |
598 | load_blacklist(); | 605 | load_blacklist(); |
@@ -641,9 +648,8 @@ DIR *opendir(const char *pathname) { | |||
641 | } | 648 | } |
642 | 649 | ||
643 | // chdir | 650 | // chdir |
644 | // definition of orig_chdir placed before storage_find function | 651 | typedef int (*orig_chdir_t)(const char *pathname); |
645 | //typedef int (*orig_chdir_t)(const char *pathname); | 652 | static orig_chdir_t orig_chdir = NULL; |
646 | //static orig_chdir_t orig_chdir = NULL; | ||
647 | int chdir(const char *pathname) { | 653 | int chdir(const char *pathname) { |
648 | #ifdef DEBUG | 654 | #ifdef DEBUG |
649 | printf("%s %s\n", __FUNCTION__, pathname); | 655 | printf("%s %s\n", __FUNCTION__, pathname); |
@@ -662,3 +668,32 @@ int chdir(const char *pathname) { | |||
662 | int rv = orig_chdir(pathname); | 668 | int rv = orig_chdir(pathname); |
663 | return rv; | 669 | return rv; |
664 | } | 670 | } |
671 | |||
672 | // fchdir | ||
673 | typedef int (*orig_fchdir_t)(int fd); | ||
674 | static orig_fchdir_t orig_fchdir = NULL; | ||
675 | int fchdir(int fd) { | ||
676 | #ifdef DEBUG | ||
677 | printf("%s %d\n", __FUNCTION__, fd); | ||
678 | #endif | ||
679 | if (!orig_fchdir) | ||
680 | orig_fchdir = (orig_fchdir_t)dlsym(RTLD_NEXT, "fchdir"); | ||
681 | |||
682 | free(cwd); | ||
683 | char *pathname=malloc(PATH_MAX); | ||
684 | if (pathname) { | ||
685 | if (snprintf(pathname,PATH_MAX,"/proc/self/fd/%d", fd)>0) { | ||
686 | cwd = realpath(pathname, NULL); | ||
687 | } else { | ||
688 | cwd = NULL; | ||
689 | fprintf(stderr, "Error: snprintf failed\n"); | ||
690 | } | ||
691 | free(pathname); | ||
692 | } else { | ||
693 | fprintf(stderr, "Error: cannot allocate memory\n"); | ||
694 | cwd = NULL; | ||
695 | } | ||
696 | |||
697 | int rv = orig_fchdir(fd); | ||
698 | return rv; | ||
699 | } | ||
diff --git a/src/man/firecfg.txt b/src/man/firecfg.txt index decc1af73..b9d336c4c 100644 --- a/src/man/firecfg.txt +++ b/src/man/firecfg.txt | |||
@@ -10,19 +10,25 @@ sandbox applications automatically, just by clicking on a regular desktop | |||
10 | menus and icons. | 10 | menus and icons. |
11 | 11 | ||
12 | The symbolic links are placed in /usr/local/bin. For more information, see | 12 | The symbolic links are placed in /usr/local/bin. For more information, see |
13 | DESKTOP INTEGRATION section in man 1 firejail. | 13 | \fBDESKTOP INTEGRATION\fR section in \fBman 1 firejail\fR. |
14 | 14 | ||
15 | .SH OPTIONS | 15 | .SH OPTIONS |
16 | .TP | 16 | .TP |
17 | \fB\-\-clean | 17 | \fB\-\-clean |
18 | Remove all firejail symbolic links. | 18 | Remove all firejail symbolic links. |
19 | .TP | 19 | .TP |
20 | \fB\-\-debug | ||
21 | Print debug messages. | ||
22 | .TP | ||
20 | \fB\-?\fR, \fB\-\-help\fR | 23 | \fB\-?\fR, \fB\-\-help\fR |
21 | Print options end exit. | 24 | Print options end exit. |
22 | .TP | 25 | .TP |
23 | \fB\-\-list | 26 | \fB\-\-list |
24 | List all firejail symbolic links | 27 | List all firejail symbolic links |
25 | .TP | 28 | .TP |
29 | \fB\-\-fix | ||
30 | Fix .desktop files. Some .desktop files use full path to executable. Firecfg will check .desktop files in /usr/share/applications/, replace full path by name if it is in PATH, and write result to $HOME/.local/share/applications/. | ||
31 | .TP | ||
26 | \fB\-\-version | 32 | \fB\-\-version |
27 | Print program version and exit. | 33 | Print program version and exit. |
28 | 34 | ||
@@ -48,13 +54,22 @@ $ firecfg --list | |||
48 | .br | 54 | .br |
49 | [...] | 55 | [...] |
50 | .br | 56 | .br |
51 | $ sudo firecfg --clear | 57 | $ sudo firecfg --clean |
52 | .br | 58 | .br |
53 | /usr/local/bin/firefox removed | 59 | /usr/local/bin/firefox removed |
54 | .br | 60 | .br |
55 | /usr/local/bin/vlc removed | 61 | /usr/local/bin/vlc removed |
56 | .br | 62 | .br |
57 | [...] | 63 | [...] |
64 | .br | ||
65 | $ firecfg --fix | ||
66 | .br | ||
67 | /home/user/.local/share/applications/chromium.desktop created | ||
68 | .br | ||
69 | /home/user/.local/share/applications/vlc.desktop created | ||
70 | .br | ||
71 | [...] | ||
72 | |||
58 | 73 | ||
59 | .SH LICENSE | 74 | .SH LICENSE |
60 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. | 75 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
@@ -65,6 +80,5 @@ Homepage: http://firejail.wordpress.com | |||
65 | \&\flfiremon\fR\|(1), | 80 | \&\flfiremon\fR\|(1), |
66 | \&\flfirejail-profile\fR\|(5), | 81 | \&\flfirejail-profile\fR\|(5), |
67 | \&\flfirejail-login\fR\|(5) | 82 | \&\flfirejail-login\fR\|(5) |
68 | \&\flfirejail-config\fR\|(5) | ||
69 | 83 | ||
70 | 84 | ||
diff --git a/src/man/firejail-config.txt b/src/man/firejail-config.txt deleted file mode 100644 index fcf4109ee..000000000 --- a/src/man/firejail-config.txt +++ /dev/null | |||
@@ -1,81 +0,0 @@ | |||
1 | .TH FIREJAIL-CONFIG 5 "MONTH YEAR" "VERSION" "firejail.config man page" | ||
2 | .SH NAME | ||
3 | firejail.config \- Firejail run time configuration file | ||
4 | |||
5 | .SH DESCRIPTION | ||
6 | /etc/firejail/firejail.config is the system-wide configuration file for Firejail. | ||
7 | It allows the system administrator to enable or disable a number of | ||
8 | features and Linux kernel security technologies used by Firejail sandbox. | ||
9 | The file contains keyword-argument pairs, one per line. | ||
10 | Use 'yes' or 'no' as configuration values. | ||
11 | |||
12 | Note that some of these features can also be enabled or disabled at compile | ||
13 | time. Most features are enabled by default both at compile time and | ||
14 | at run time. | ||
15 | |||
16 | .TP | ||
17 | \fBbind | ||
18 | Enable or disable bind support, default enabled. | ||
19 | |||
20 | .TP | ||
21 | \fBchroot | ||
22 | Enable or disable chroot support, default enabled. | ||
23 | |||
24 | .TP | ||
25 | \fBfile-transfer | ||
26 | Enable or disable file transfer support, default enabled. | ||
27 | |||
28 | .TP | ||
29 | \fBnetwork | ||
30 | Enable or disable networking features, default enabled. | ||
31 | |||
32 | .TP | ||
33 | \fBrestricted-network | ||
34 | Enable or disable restricted network support, default disabled. If enabled, | ||
35 | networking features should also be enabled (network yes). | ||
36 | Restricted networking grants access to --interface and --net=ethXXX | ||
37 | only to root user. Regular users are only allowed --net=none. | ||
38 | |||
39 | .TP | ||
40 | \fBsecomp | ||
41 | Enable or disable seccomp support, default enabled. | ||
42 | |||
43 | .TP | ||
44 | \fBuserns | ||
45 | Enable or disable user namespace support, default enabled. | ||
46 | |||
47 | .TP | ||
48 | \fBx11 | ||
49 | Enable or disable X11 sandboxing support, default enabled. | ||
50 | |||
51 | .TP | ||
52 | \fBxephyr-screen | ||
53 | Screen size for --x11=xephyr, default 800x600. Run /usr/bin/xrandr for | ||
54 | a full list of resolutions available on your specific setup. Examples: | ||
55 | .br | ||
56 | |||
57 | .br | ||
58 | xephyr-screen 640x480 | ||
59 | .br | ||
60 | xephyr-screen 800x600 | ||
61 | .br | ||
62 | xephyr-screen 1024x768 | ||
63 | .br | ||
64 | xephyr-screen 1280x1024 | ||
65 | |||
66 | .SH FILES | ||
67 | /etc/firejail/firejail.config | ||
68 | |||
69 | .SH LICENSE | ||
70 | Firejail is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. | ||
71 | .PP | ||
72 | Homepage: http://firejail.wordpress.com | ||
73 | .SH SEE ALSO | ||
74 | \&\flfirejail\fR\|(1), | ||
75 | \&\flfiremon\fR\|(1), | ||
76 | \&\flfirecfg\fR\|(1), | ||
77 | \&\flfirejail-profile\fR\|(5) | ||
78 | \&\flfirejail-login\fR\|(5) | ||
79 | |||
80 | |||
81 | |||
diff --git a/src/man/firejail-login.txt b/src/man/firejail-login.txt index 6cd9ce3cb..796179d0b 100644 --- a/src/man/firejail-login.txt +++ b/src/man/firejail-login.txt | |||
@@ -13,9 +13,13 @@ Example: | |||
13 | 13 | ||
14 | netblue:--net=none --protocol=unix | 14 | netblue:--net=none --protocol=unix |
15 | 15 | ||
16 | Wildcard patterns are accepted in the user name field: | ||
17 | |||
18 | user*: --private | ||
19 | |||
16 | .SH RESTRICTED SHELL | 20 | .SH RESTRICTED SHELL |
17 | To configure a restricted shell, replace /bin/bash with /usr/bin/firejail in | 21 | To configure a restricted shell, replace /bin/bash with /usr/bin/firejail in |
18 | /etc/password file for each user that needs to be restricted. Alternatively, | 22 | /etc/passwd file for each user that needs to be restricted. Alternatively, |
19 | you can specify /usr/bin/firejail using adduser or usermod commands: | 23 | you can specify /usr/bin/firejail using adduser or usermod commands: |
20 | 24 | ||
21 | adduser \-\-shell /usr/bin/firejail username | 25 | adduser \-\-shell /usr/bin/firejail username |
@@ -34,6 +38,5 @@ Homepage: http://firejail.wordpress.com | |||
34 | \&\flfiremon\fR\|(1), | 38 | \&\flfiremon\fR\|(1), |
35 | \&\flfirecfg\fR\|(1), | 39 | \&\flfirecfg\fR\|(1), |
36 | \&\flfirejail-profile\fR\|(5) | 40 | \&\flfirejail-profile\fR\|(5) |
37 | \&\flfirejail-config\fR\|(5) | ||
38 | 41 | ||
39 | 42 | ||
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index 9045c1122..d6113218c 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt | |||
@@ -44,7 +44,7 @@ To disable default profile loading, use --noprofile command option. Example: | |||
44 | .RS | 44 | .RS |
45 | $ firejail | 45 | $ firejail |
46 | .br | 46 | .br |
47 | Reading profile /etc/firejail/generic.profile | 47 | Reading profile /etc/firejail/default.profile |
48 | .br | 48 | .br |
49 | Parent pid 8553, child pid 8554 | 49 | Parent pid 8553, child pid 8554 |
50 | .br | 50 | .br |
@@ -93,11 +93,17 @@ If the file name matches file_name, the file will not be blacklisted in any blac | |||
93 | Example: "noblacklist ${HOME}/.mozilla" | 93 | Example: "noblacklist ${HOME}/.mozilla" |
94 | 94 | ||
95 | .TP | 95 | .TP |
96 | \fBignore command | 96 | \fBignore |
97 | Ignore command. | 97 | Ignore command. |
98 | 98 | ||
99 | Example: "ignore seccomp" | 99 | Example: "ignore seccomp" |
100 | 100 | ||
101 | .TP | ||
102 | \fBquiet | ||
103 | Disable Firejail's output. This should be the first uncommented command in the profile file. | ||
104 | |||
105 | Example: "quiet" | ||
106 | |||
101 | .SH Filesystem | 107 | .SH Filesystem |
102 | These profile entries define a chroot filesystem built on top of the existing | 108 | These profile entries define a chroot filesystem built on top of the existing |
103 | host filesystem. Each line describes a file element that is removed from | 109 | host filesystem. Each line describes a file element that is removed from |
@@ -122,11 +128,16 @@ blacklist ${PATH}/ifconfig | |||
122 | blacklist ${HOME}/.ssh | 128 | blacklist ${HOME}/.ssh |
123 | 129 | ||
124 | .TP | 130 | .TP |
125 | \fBread-only file_or_directory | 131 | \fBblacklist-nolog file_or_directory |
126 | Make directory or file read-only. | 132 | When --tracelog flag is set, blacklisting generates syslog messages if the sandbox tries to access the file or directory. |
127 | .TP | 133 | blacklist-nolog command disables syslog messages for this particular file or directory. Examples: |
128 | \fBtmpfs directory | 134 | .br |
129 | Mount an empty tmpfs filesystem on top of directory. This option is available only when running the sandbox as root. | 135 | |
136 | .br | ||
137 | blacklist-nolog /usr/bin | ||
138 | .br | ||
139 | blacklist-nolog /usr/bin/gcc* | ||
140 | |||
130 | .TP | 141 | .TP |
131 | \fBbind directory1,directory2 | 142 | \fBbind directory1,directory2 |
132 | Mount-bind directory1 on top of directory2. This option is only available when running as root. | 143 | Mount-bind directory1 on top of directory2. This option is only available when running as root. |
@@ -135,8 +146,14 @@ Mount-bind directory1 on top of directory2. This option is only available when r | |||
135 | Mount-bind file1 on top of file2. This option is only available when running as root. | 146 | Mount-bind file1 on top of file2. This option is only available when running as root. |
136 | .TP | 147 | .TP |
137 | \fBmkdir directory | 148 | \fBmkdir directory |
138 | Create a directory in user home. Use this command for whitelisted directories you need to preserve | 149 | Create a directory in user home before the sandbox is started. |
139 | when the sandbox is closed. Subdirectories also need to be created using mkdir. Example from | 150 | The directory is created if it doesn't already exist. |
151 | .br | ||
152 | |||
153 | .br | ||
154 | Use this command for whitelisted directories you need to preserve | ||
155 | when the sandbox is closed. Without it, the application will create the directory, and the directory | ||
156 | will be deleted when the sandbox is closed. Subdirectories are recursively created. Example from | ||
140 | firefox profile: | 157 | firefox profile: |
141 | .br | 158 | .br |
142 | 159 | ||
@@ -145,14 +162,17 @@ mkdir ~/.mozilla | |||
145 | .br | 162 | .br |
146 | whitelist ~/.mozilla | 163 | whitelist ~/.mozilla |
147 | .br | 164 | .br |
148 | mkdir ~/.cache | ||
149 | .br | ||
150 | mkdir ~/.cache/mozilla | ||
151 | .br | ||
152 | mkdir ~/.cache/mozilla/firefox | 165 | mkdir ~/.cache/mozilla/firefox |
153 | .br | 166 | .br |
154 | whitelist ~/.cache/mozilla/firefox | 167 | whitelist ~/.cache/mozilla/firefox |
155 | .TP | 168 | .TP |
169 | \fBmkfile file | ||
170 | Similar to mkdir, this command creates a file in user home before the sandbox is started. | ||
171 | The file is created if it doesn't already exist, but it's target directory has to exist. | ||
172 | .TP | ||
173 | \fBnoexec file_or_directory | ||
174 | Remount the file or the directory noexec, nodev and nosuid. | ||
175 | .TP | ||
156 | \fBprivate | 176 | \fBprivate |
157 | Mount new /root and /home/user directories in temporary | 177 | Mount new /root and /home/user directories in temporary |
158 | filesystems. All modifications are discarded when the sandbox is | 178 | filesystems. All modifications are discarded when the sandbox is |
@@ -161,6 +181,12 @@ closed. | |||
161 | \fBprivate directory | 181 | \fBprivate directory |
162 | Use directory as user home. | 182 | Use directory as user home. |
163 | .TP | 183 | .TP |
184 | \f\private-home file,directory | ||
185 | Build a new user home in a temporary | ||
186 | filesystem, and copy the files and directories in the list in the | ||
187 | new home. All modifications are discarded when the sandbox is | ||
188 | closed. | ||
189 | .TP | ||
164 | \fBprivate-bin file,file | 190 | \fBprivate-bin file,file |
165 | Build a new /bin in a temporary filesystem, and copy the programs in the list. | 191 | Build a new /bin in a temporary filesystem, and copy the programs in the list. |
166 | The same directory is also bind-mounted over /sbin, /usr/bin and /usr/sbin. | 192 | The same directory is also bind-mounted over /sbin, /usr/bin and /usr/sbin. |
@@ -174,19 +200,43 @@ filesystem, and copy the files and directories in the list. | |||
174 | All modifications are discarded when the sandbox is closed. | 200 | All modifications are discarded when the sandbox is closed. |
175 | .TP | 201 | .TP |
176 | \fBprivate-tmp | 202 | \fBprivate-tmp |
177 | Mount an empty temporary filesystem on top of /tmp directory. | 203 | Mount an empty temporary filesystem on top of /tmp directory whitelisting /tmp/.X11-unix. |
178 | .TP | 204 | .TP |
179 | \fBwhitelist file_or_directory | 205 | \fBread-only file_or_directory |
180 | Build a new user home in a temporary filesystem, and mount-bind file_or_directory. | 206 | Make directory or file read-only. |
181 | The modifications to file_or_directory are persistent, everything else is discarded | 207 | .TP |
182 | when the sandbox is closed. | 208 | \fBread-write file_or_directory |
209 | Make directory or file read-write. | ||
210 | .TP | ||
211 | \fBtmpfs directory | ||
212 | Mount an empty tmpfs filesystem on top of directory. This option is available only when running the sandbox as root. | ||
183 | .TP | 213 | .TP |
184 | \fBtracelog | 214 | \fBtracelog |
185 | Blacklist violations logged to syslog. | 215 | Blacklist violations logged to syslog. |
216 | .TP | ||
217 | \fBwhitelist file_or_directory | ||
218 | Whitelist directory or file. A temporary file system is mounted on the top directory, and the | ||
219 | whitelisted files are mount-binded inside. Modifications to whitelisted files are persistent, | ||
220 | everything else is discarded when the sandbox is closed. The top directory could be | ||
221 | user home, /dev, /media, /mnt, /opt, /srv, /var, and /tmp. | ||
222 | .br | ||
223 | |||
224 | .br | ||
225 | Symbolic link handling: with the exception of user home, both the link and the real file should be in | ||
226 | the same top directory. For user home, both the link and the real file should be owned by the user. | ||
227 | .TP | ||
228 | \fBwritable-etc | ||
229 | Mount /etc directory read-write. | ||
230 | .TP | ||
231 | \fBwritable-var | ||
232 | Mount /var directory read-write. | ||
186 | .SH Security filters | 233 | .SH Security filters |
187 | The following security filters are currently implemented: | 234 | The following security filters are currently implemented: |
188 | 235 | ||
189 | .TP | 236 | .TP |
237 | \fBapparmor | ||
238 | Enable AppArmor confinement. | ||
239 | .TP | ||
190 | \fBcaps | 240 | \fBcaps |
191 | Enable default Linux capabilities filter. | 241 | Enable default Linux capabilities filter. |
192 | .TP | 242 | .TP |
@@ -205,10 +255,7 @@ first argument to socket system call. Recognized values: \fBunix\fR, | |||
205 | \fBinet\fR, \fBinet6\fR, \fBnetlink\fR and \fBpacket\fR. | 255 | \fBinet\fR, \fBinet6\fR, \fBnetlink\fR and \fBpacket\fR. |
206 | .TP | 256 | .TP |
207 | \fBseccomp | 257 | \fBseccomp |
208 | Enable default seccomp filter. The default list is as follows: | 258 | Enable seccomp filter and blacklist the syscalls in the default list. See man 1 firejail for more details. |
209 | mount, umount2, ptrace, kexec_load, open_by_handle_at, init_module, finit_module, delete_module, | ||
210 | iopl, ioperm, swapon, swapoff, syslog, process_vm_readv and process_vm_writev, | ||
211 | sysfs,_sysctl, adjtimex, clock_adjtime, lookup_dcookie, perf_event_open, fanotify_init and kcmp. | ||
212 | .TP | 259 | .TP |
213 | \fBseccomp syscall,syscall,syscall | 260 | \fBseccomp syscall,syscall,syscall |
214 | Enable seccomp filter and blacklist the system calls in the list on top of default seccomp filter. | 261 | Enable seccomp filter and blacklist the system calls in the list on top of default seccomp filter. |
@@ -219,9 +266,32 @@ Enable seccomp filter and blacklist the system calls in the list. | |||
219 | \fBseccomp.keep syscall,syscall,syscall | 266 | \fBseccomp.keep syscall,syscall,syscall |
220 | Enable seccomp filter and whitelist the system calls in the list. | 267 | Enable seccomp filter and whitelist the system calls in the list. |
221 | .TP | 268 | .TP |
269 | \fBnonewprivs | ||
270 | Sets the NO_NEW_PRIVS prctl. This ensures that child processes | ||
271 | cannot acquire new privileges using execve(2); in particular, | ||
272 | this means that calling a suid binary (or one with file capabilities) | ||
273 | does not result in an increase of privilege. | ||
274 | .TP | ||
222 | \fBnoroot | 275 | \fBnoroot |
223 | Use this command to enable an user namespace. The namespace has only one user, the current user. | 276 | Use this command to enable an user namespace. The namespace has only one user, the current user. |
224 | There is no root account (uid 0) defined in the namespace. | 277 | There is no root account (uid 0) defined in the namespace. |
278 | .TP | ||
279 | \fBx11 | ||
280 | Enable X11 sandboxing. | ||
281 | .TP | ||
282 | \fBx11 none | ||
283 | Blacklist /tmp/.X11-unix directory, ${HOME}/.Xauthority and file specified in ${XAUTHORITY} environment variable. | ||
284 | Remove DISPLAY and XAUTHORITY environment variables. | ||
285 | Stop with error message if X11 abstract socket will be accessible in jail. | ||
286 | .TP | ||
287 | \fBx11 xephyr | ||
288 | Enable X11 sandboxing with xephyr. | ||
289 | .TP | ||
290 | \fBx11 xorg | ||
291 | Enable X11 sandboxing with X11 security extension. | ||
292 | .TP | ||
293 | \fBx11 xpra | ||
294 | Enable X11 sandboxing with xpra. | ||
225 | 295 | ||
226 | .SH Resource limits, CPU affinity, Control Groups | 296 | .SH Resource limits, CPU affinity, Control Groups |
227 | These profile entries define the limits on system resources (rlimits) for the processes inside the sandbox. | 297 | These profile entries define the limits on system resources (rlimits) for the processes inside the sandbox. |
@@ -255,6 +325,10 @@ The sandbox is placed in g1 control group. | |||
255 | 325 | ||
256 | .SH User Environment | 326 | .SH User Environment |
257 | .TP | 327 | .TP |
328 | \fBallusers | ||
329 | All user home directories are visible inside the sandbox. By default, only current user home directory is visible. | ||
330 | |||
331 | .TP | ||
258 | \fBname sandboxname | 332 | \fBname sandboxname |
259 | Set sandbox name. Example: | 333 | Set sandbox name. Example: |
260 | .br | 334 | .br |
@@ -284,9 +358,18 @@ Enable IPC namespace. | |||
284 | .TP | 358 | .TP |
285 | \fBnosound | 359 | \fBnosound |
286 | Disable sound system. | 360 | Disable sound system. |
361 | .TP | ||
362 | \fBno3d | ||
363 | Disable 3D hardware acceleration. | ||
364 | |||
287 | .SH Networking | 365 | .SH Networking |
288 | Networking features available in profile files. | 366 | Networking features available in profile files. |
289 | 367 | ||
368 | .TP | ||
369 | \fBdefaultgw address | ||
370 | Use this address as default gateway in the new network namespace. | ||
371 | |||
372 | .TP | ||
290 | \fBdns address | 373 | \fBdns address |
291 | Set a DNS server for the sandbox. Up to three DNS servers can be defined. | 374 | Set a DNS server for the sandbox. Up to three DNS servers can be defined. |
292 | 375 | ||
@@ -295,6 +378,45 @@ Set a DNS server for the sandbox. Up to three DNS servers can be defined. | |||
295 | Set a hostname for the sandbox. | 378 | Set a hostname for the sandbox. |
296 | 379 | ||
297 | .TP | 380 | .TP |
381 | \fBip address | ||
382 | Assign IP addresses to the last network interface defined by a net command. A | ||
383 | default gateway is assigned by default. | ||
384 | .br | ||
385 | |||
386 | .br | ||
387 | Example: | ||
388 | .br | ||
389 | net eth0 | ||
390 | .br | ||
391 | ip 10.10.20.56 | ||
392 | |||
393 | .TP | ||
394 | \fBip none | ||
395 | No IP address and no default gateway are configured for the last interface | ||
396 | defined by a net command. Use this option | ||
397 | in case you intend to start an external DHCP client in the sandbox. | ||
398 | .br | ||
399 | |||
400 | .br | ||
401 | Example: | ||
402 | .br | ||
403 | net eth0 | ||
404 | .br | ||
405 | ip none | ||
406 | |||
407 | .TP | ||
408 | \fBip6 address | ||
409 | Assign IPv6 addresses to the last network interface defined by a net command. | ||
410 | .br | ||
411 | |||
412 | .br | ||
413 | Example: | ||
414 | .br | ||
415 | net eth0 | ||
416 | .br | ||
417 | ip6 2001:0db8:0:f101::1/64 | ||
418 | |||
419 | .TP | ||
298 | \fBiprange address,address | 420 | \fBiprange address,address |
299 | Assign an IP address in the provided range to the last network | 421 | Assign an IP address in the provided range to the last network |
300 | interface defined by a net command. A default gateway is assigned by default. | 422 | interface defined by a net command. A default gateway is assigned by default. |
@@ -311,6 +433,16 @@ iprange 192.168.1.150,192.168.1.160 | |||
311 | .br | 433 | .br |
312 | 434 | ||
313 | .TP | 435 | .TP |
436 | \fBmac address | ||
437 | Assign MAC addresses to the last network interface defined by a net command. | ||
438 | |||
439 | .TP | ||
440 | \fBmtu number | ||
441 | Assign a MTU value to the last network interface defined by a net command. | ||
442 | |||
443 | |||
444 | |||
445 | .TP | ||
314 | \fBnetfilter | 446 | \fBnetfilter |
315 | If a new network namespace is created, enabled default network filter. | 447 | If a new network namespace is created, enabled default network filter. |
316 | 448 | ||
@@ -345,6 +477,17 @@ available in the new namespace is a new loopback interface (lo). | |||
345 | Use this option to deny network access to programs that don't | 477 | Use this option to deny network access to programs that don't |
346 | really need network access. | 478 | really need network access. |
347 | 479 | ||
480 | .TP | ||
481 | \fBveth-name name | ||
482 | Use this name for the interface connected to the bridge for --net=bridge_interface commands, | ||
483 | instead of the default one. | ||
484 | |||
485 | .SH Other | ||
486 | .TP | ||
487 | \fBjoin-or-start sandboxname | ||
488 | Join the sandbox identified by name or start a new one. | ||
489 | Same as "firejail --join=sandboxname" command if sandbox with specified name exists, otherwise same as "name sandboxname". | ||
490 | |||
348 | .SH RELOCATING PROFILES | 491 | .SH RELOCATING PROFILES |
349 | For various reasons some users might want to keep the profile files in a different directory. | 492 | For various reasons some users might want to keep the profile files in a different directory. |
350 | Using \fB--profile-path\fR command line option, Firejail can be instructed to look for profiles | 493 | Using \fB--profile-path\fR command line option, Firejail can be instructed to look for profiles |
@@ -388,7 +531,6 @@ Homepage: http://firejail.wordpress.com | |||
388 | \&\flfiremon\fR\|(1), | 531 | \&\flfiremon\fR\|(1), |
389 | \&\flfirecfg\fR\|(1), | 532 | \&\flfirecfg\fR\|(1), |
390 | \&\flfirejail-login\fR\|(5) | 533 | \&\flfirejail-login\fR\|(5) |
391 | \&\flfirejail-config\fR\|(5) | ||
392 | 534 | ||
393 | 535 | ||
394 | 536 | ||
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 23db832c1..bb9ae270c 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -11,7 +11,7 @@ firejail [OPTIONS] [program and arguments] | |||
11 | File transfer from an existing sandbox | 11 | File transfer from an existing sandbox |
12 | .PP | 12 | .PP |
13 | .RS | 13 | .RS |
14 | firejail {\-\-ls | \-\-get} dir_or_filename | 14 | firejail {\-\-ls | \-\-get | \-\-put} dir_or_filename |
15 | .RE | 15 | .RE |
16 | .PP | 16 | .PP |
17 | Network traffic shaping for an existing sandbox: | 17 | Network traffic shaping for an existing sandbox: |
@@ -50,15 +50,16 @@ of applications. The software includes security profiles for a number of more co | |||
50 | Linux programs, such as Mozilla Firefox, Chromium, VLC, Transmission etc. | 50 | Linux programs, such as Mozilla Firefox, Chromium, VLC, Transmission etc. |
51 | 51 | ||
52 | .SH USAGE | 52 | .SH USAGE |
53 | Without any options, the sandbox consists of a chroot filesystem build in a new mount namespace, | 53 | Without any options, the sandbox consists of a filesystem build in a new mount namespace, |
54 | and new PID and UTS namespaces. IPC, network and user namespaces can be added using the command line options. | 54 | and new PID and UTS namespaces. IPC, network and user namespaces can be added using the |
55 | The default Firejail filesystem is based on the host filesystem with the main directories mounted read-only. | 55 | command line options. The default Firejail filesystem is based on the host filesystem with the main |
56 | Only /home and /tmp are writable. | 56 | system directories mounted read-only. These directories are /etc, /var, /usr, /bin, /sbin, /lib, /lib32, |
57 | /libx32 and /lib64. Only /home and /tmp are writable. | ||
57 | .PP | 58 | .PP |
58 | As it starts up, Firejail tries to find a security profile based on the name of the application. | 59 | As it starts up, Firejail tries to find a security profile based on the name of the application. |
59 | If an appropriate profile is not found, Firejail will use a default profile. | 60 | If an appropriate profile is not found, Firejail will use a default profile. |
60 | The default profile is quite restrictive. In case the application doesn't work, use --noprofile option | 61 | The default profile is quite restrictive. In case the application doesn't work, use --noprofile option |
61 | to disable it. For more information, please see \fBSECURITY PROFILES\fR section. | 62 | to disable it. For more information, please see \fBSECURITY PROFILES\fR section below. |
62 | .PP | 63 | .PP |
63 | If a program argument is not specified, Firejail starts /bin/bash shell. | 64 | If a program argument is not specified, Firejail starts /bin/bash shell. |
64 | Examples: | 65 | Examples: |
@@ -74,6 +75,46 @@ $ firejail [OPTIONS] firefox # starting Mozilla Firefox | |||
74 | \fB\-\- | 75 | \fB\-\- |
75 | Signal the end of options and disables further option processing. | 76 | Signal the end of options and disables further option processing. |
76 | .TP | 77 | .TP |
78 | \fB\-\-allow-debuggers | ||
79 | Allow tools such as strace and gdb inside the sandbox. | ||
80 | .br | ||
81 | |||
82 | .br | ||
83 | Example: | ||
84 | .br | ||
85 | $ firejail --allow-debuggers --profile=/etc/firejail/firefox.profile strace -f firefox | ||
86 | .TP | ||
87 | \fB\-\-allusers | ||
88 | All user home directories are visible inside the sandbox. By default, only current user home directory is visible. | ||
89 | .br | ||
90 | |||
91 | .br | ||
92 | Example: | ||
93 | .br | ||
94 | $ firejail --allusers | ||
95 | .TP | ||
96 | \fB\-\-apparmor | ||
97 | Enable AppArmor confinement. For more information, please see \fBAPPARMOR\fR section below. | ||
98 | .TP | ||
99 | \fB\-\-appimage | ||
100 | Sandbox an AppImage (http://appimage.org/) application. | ||
101 | .br | ||
102 | |||
103 | .br | ||
104 | Example: | ||
105 | .br | ||
106 | $ firejail --appimage krita-3.0-x86_64.appimage | ||
107 | .br | ||
108 | $ firejail --appimage --private krita-3.0-x86_64.appimage | ||
109 | .br | ||
110 | $ firejail --appimage --net=none --x11 krita-3.0-x86_64.appimage | ||
111 | .TP | ||
112 | \fB\-\-audit | ||
113 | Audit the sandbox, see \fBAUDIT\fR section for more details. | ||
114 | .TP | ||
115 | \fB\-\-audit=test-program | ||
116 | Audit the sandbox, see \fBAUDIT\fR section for more details. | ||
117 | .TP | ||
77 | \fB\-\-bandwidth=name|pid | 118 | \fB\-\-bandwidth=name|pid |
78 | Set bandwidth limits for the sandbox identified by name or PID, see \fBTRAFFIC SHAPING\fR section for more details. | 119 | Set bandwidth limits for the sandbox identified by name or PID, see \fBTRAFFIC SHAPING\fR section for more details. |
79 | .TP | 120 | .TP |
@@ -152,14 +193,7 @@ Example: | |||
152 | .br | 193 | .br |
153 | $ sudo firejail \-\-caps.keep=chown,net_bind_service,setgid,\\ | 194 | $ sudo firejail \-\-caps.keep=chown,net_bind_service,setgid,\\ |
154 | setuid /etc/init.d/nginx start | 195 | setuid /etc/init.d/nginx start |
155 | .br | ||
156 | 196 | ||
157 | .br | ||
158 | A short note about mixing \-\-whitelist and \-\-read-only options. Whitelisted directories | ||
159 | should be made read-only independently. Making a parent directory read-only, will not | ||
160 | make the whitelist read-only. Example: | ||
161 | .br | ||
162 | $ firejail --whitelist=~/work --read-only=~/ --read-only=~/work | ||
163 | .TP | 197 | .TP |
164 | \fB\-\-caps.print=name|pid | 198 | \fB\-\-caps.print=name|pid |
165 | Print the caps filter for the sandbox identified by name or by PID. | 199 | Print the caps filter for the sandbox identified by name or by PID. |
@@ -194,7 +228,8 @@ Example: | |||
194 | 228 | ||
195 | .TP | 229 | .TP |
196 | \fB\-\-chroot=dirname | 230 | \fB\-\-chroot=dirname |
197 | Chroot the sandbox into a root filesystem. If the sandbox is started as a | 231 | Chroot the sandbox into a root filesystem. Unlike the regular filesystem container, |
232 | the system directories are mounted read-write. If the sandbox is started as a | ||
198 | regular user, default seccomp and capabilities filters are enabled. This | 233 | regular user, default seccomp and capabilities filters are enabled. This |
199 | option is not available on Grsecurity systems. | 234 | option is not available on Grsecurity systems. |
200 | .br | 235 | .br |
@@ -465,6 +500,11 @@ in case you intend to start an external DHCP client in the sandbox. | |||
465 | Example: | 500 | Example: |
466 | .br | 501 | .br |
467 | $ firejail \-\-net=eth0 \-\-\ip=none | 502 | $ firejail \-\-net=eth0 \-\-\ip=none |
503 | .br | ||
504 | |||
505 | .br | ||
506 | If the corresponding interface doesn't have an IP address configured, this | ||
507 | option is enabled by default. | ||
468 | 508 | ||
469 | .TP | 509 | .TP |
470 | \fB\-\-ip6=address | 510 | \fB\-\-ip6=address |
@@ -547,19 +587,19 @@ $ firejail --net=eth0 --name=browser firefox & | |||
547 | .br | 587 | .br |
548 | # change netfilter configuration | 588 | # change netfilter configuration |
549 | .br | 589 | .br |
550 | $ sudo firejail --join-network=browser "cat /etc/firejail/nolocal.net | /sbin/iptables-restore" | 590 | $ sudo firejail --join-network=browser bash -c "cat /etc/firejail/nolocal.net | /sbin/iptables-restore" |
551 | .br | 591 | .br |
552 | 592 | ||
553 | .br | 593 | .br |
554 | # verify netfilter configuration | 594 | # verify netfilter configuration |
555 | .br | 595 | .br |
556 | $ sudo firejail --join-network=browser "/sbin/iptables -vL" | 596 | $ sudo firejail --join-network=browser /sbin/iptables -vL |
557 | .br | 597 | .br |
558 | 598 | ||
559 | .br | 599 | .br |
560 | # verify IP addresses | 600 | # verify IP addresses |
561 | .br | 601 | .br |
562 | $ sudo firejail --join-network=browser "ip addr" | 602 | $ sudo firejail --join-network=browser ip addr |
563 | .br | 603 | .br |
564 | Switching to pid 1932, the first child process inside the sandbox | 604 | Switching to pid 1932, the first child process inside the sandbox |
565 | .br | 605 | .br |
@@ -588,6 +628,13 @@ Switching to pid 1932, the first child process inside the sandbox | |||
588 | valid_lft forever preferred_lft forever | 628 | valid_lft forever preferred_lft forever |
589 | 629 | ||
590 | .TP | 630 | .TP |
631 | \fB\-\-join-or-start=name | ||
632 | Join the sandbox identified by name or start a new one. | ||
633 | Same as "firejail --join=name" if sandbox with specified name exists, otherwise same as "firejail --name=name ..." | ||
634 | .br | ||
635 | Note that in contrary to other join options there is respective profile option. | ||
636 | |||
637 | .TP | ||
591 | \fB\-\-ls=name|pid dir_or_filename | 638 | \fB\-\-ls=name|pid dir_or_filename |
592 | List files in sandbox container, see \fBFILE TRANSFER\fR section for more details. | 639 | List files in sandbox container, see \fBFILE TRANSFER\fR section for more details. |
593 | 640 | ||
@@ -798,13 +845,23 @@ PID User RX(KB/s) TX(KB/s) Command | |||
798 | .TP | 845 | .TP |
799 | \fB\-\-nice=value | 846 | \fB\-\-nice=value |
800 | Set nice value for all processes running inside the sandbox. | 847 | Set nice value for all processes running inside the sandbox. |
848 | Only root may specify a negative value. | ||
801 | .br | 849 | .br |
802 | 850 | ||
803 | .br | 851 | .br |
804 | Example: | 852 | Example: |
805 | .br | 853 | .br |
806 | $ firejail --nice=-5 firefox | 854 | $ firejail --nice=2 firefox |
855 | |||
856 | .TP | ||
857 | \fB\-\-no3d | ||
858 | Disable 3D hardware acceleration. | ||
859 | .br | ||
807 | 860 | ||
861 | .br | ||
862 | Example: | ||
863 | .br | ||
864 | $ firejail --no3d firefox | ||
808 | 865 | ||
809 | .TP | 866 | .TP |
810 | \fB\-\-noblacklist=dirname_or_filename | 867 | \fB\-\-noblacklist=dirname_or_filename |
@@ -831,6 +888,21 @@ $ nc dict.org 2628 | |||
831 | 220 pan.alephnull.com dictd 1.12.1/rf on Linux 3.14-1-amd64 | 888 | 220 pan.alephnull.com dictd 1.12.1/rf on Linux 3.14-1-amd64 |
832 | .br | 889 | .br |
833 | .TP | 890 | .TP |
891 | \fB\-\-noexec=dirname_or_filename | ||
892 | Remount directory or file noexec, nodev and nosuid. | ||
893 | .br | ||
894 | |||
895 | .br | ||
896 | Example: | ||
897 | .br | ||
898 | $ firejail \-\-noexec=/tmp | ||
899 | .br | ||
900 | |||
901 | .br | ||
902 | /etc and /var are noexec by default if the sandbox was started as a regular user. If there are more than one mount operation | ||
903 | on the path of the file or directory, noexec should be applied to the last one. Always check if the change took effect inside the sandbox. | ||
904 | |||
905 | .TP | ||
834 | \fB\-\-nogroups | 906 | \fB\-\-nogroups |
835 | Disable supplementary groups. Without this option, supplementary groups are enabled for the user starting the | 907 | Disable supplementary groups. Without this option, supplementary groups are enabled for the user starting the |
836 | sandbox. For root user supplementary groups are always disabled. | 908 | sandbox. For root user supplementary groups are always disabled. |
@@ -865,7 +937,7 @@ Example: | |||
865 | .br | 937 | .br |
866 | $ firejail | 938 | $ firejail |
867 | .br | 939 | .br |
868 | Reading profile /etc/firejail/generic.profile | 940 | Reading profile /etc/firejail/default.profile |
869 | .br | 941 | .br |
870 | Parent pid 8553, child pid 8554 | 942 | Parent pid 8553, child pid 8554 |
871 | .br | 943 | .br |
@@ -908,6 +980,14 @@ ping: icmp open socket: Operation not permitted | |||
908 | $ | 980 | $ |
909 | 981 | ||
910 | .TP | 982 | .TP |
983 | \fB\-\-nonewprivs | ||
984 | Sets the NO_NEW_PRIVS prctl. This ensures that child processes | ||
985 | cannot acquire new privileges using execve(2); in particular, | ||
986 | this means that calling a suid binary (or one with file capabilities) | ||
987 | does not result in an increase of privilege. This option | ||
988 | is enabled by default if seccomp filter is activated. | ||
989 | |||
990 | .TP | ||
911 | \fB\-\-nosound | 991 | \fB\-\-nosound |
912 | Disable sound system. | 992 | Disable sound system. |
913 | .br | 993 | .br |
@@ -946,13 +1026,15 @@ $ ls -l sandboxlog* | |||
946 | 1026 | ||
947 | .TP | 1027 | .TP |
948 | \fB\-\-overlay | 1028 | \fB\-\-overlay |
949 | Mount a filesystem overlay on top of the current filesystem. All filesystem modifications go into the overlay. | 1029 | Mount a filesystem overlay on top of the current filesystem. Unlike the regular filesystem container, |
950 | The overlay is stored in $HOME/.firejail directory. This option is not available on Grsecurity systems. | 1030 | the system directories are mounted read-write. All filesystem modifications go into the overlay. |
1031 | The overlay is stored in $HOME/.firejail/<PID> directory. | ||
951 | .br | 1032 | .br |
952 | 1033 | ||
953 | .br | 1034 | .br |
954 | OverlayFS support is required in Linux kernel for this option to work. | 1035 | OverlayFS support is required in Linux kernel for this option to work. |
955 | OverlayFS was officially introduced in Linux kernel version 3.18 | 1036 | OverlayFS was officially introduced in Linux kernel version 3.18. |
1037 | This option is not available on Grsecurity systems. | ||
956 | .br | 1038 | .br |
957 | 1039 | ||
958 | .br | 1040 | .br |
@@ -961,14 +1043,34 @@ Example: | |||
961 | $ firejail \-\-overlay firefox | 1043 | $ firejail \-\-overlay firefox |
962 | 1044 | ||
963 | .TP | 1045 | .TP |
1046 | \fB\-\-overlay-named=name | ||
1047 | Mount a filesystem overlay on top of the current filesystem. Unlike the regular filesystem container, | ||
1048 | the system directories are mounted read-write. All filesystem modifications go into the overlay. | ||
1049 | The overlay is stored in $HOME/.firejail/<NAME> directory. The created overlay can be reused between multiple | ||
1050 | sessions. | ||
1051 | .br | ||
1052 | |||
1053 | .br | ||
1054 | OverlayFS support is required in Linux kernel for this option to work. | ||
1055 | OverlayFS was officially introduced in Linux kernel version 3.18. | ||
1056 | This option is not available on Grsecurity systems. | ||
1057 | .br | ||
1058 | |||
1059 | .br | ||
1060 | Example: | ||
1061 | .br | ||
1062 | $ firejail \-\-overlay-named=jail1 firefox | ||
1063 | |||
1064 | .TP | ||
964 | \fB\-\-overlay-tmpfs | 1065 | \fB\-\-overlay-tmpfs |
965 | Mount a filesystem overlay on top of the current filesystem. All filesystem modifications go into the overlay, | 1066 | Mount a filesystem overlay on top of the current filesystem. All filesystem modifications go into the overlay, |
966 | and are discarded when the sandbox is closed. This option is not available on Grsecurity systems. | 1067 | and are discarded when the sandbox is closed. |
967 | .br | 1068 | .br |
968 | 1069 | ||
969 | .br | 1070 | .br |
970 | OverlayFS support is required in Linux kernel for this option to work. | 1071 | OverlayFS support is required in Linux kernel for this option to work. |
971 | OverlayFS was officially introduced in Linux kernel version 3.18 | 1072 | OverlayFS was officially introduced in Linux kernel version 3.18. |
1073 | This option is not available on Grsecurity systems. | ||
972 | .br | 1074 | .br |
973 | 1075 | ||
974 | .br | 1076 | .br |
@@ -977,6 +1079,17 @@ Example: | |||
977 | $ firejail \-\-overlay-tmpfs firefox | 1079 | $ firejail \-\-overlay-tmpfs firefox |
978 | 1080 | ||
979 | .TP | 1081 | .TP |
1082 | \fB\-\-overlay-clean | ||
1083 | Clean all overlays stored in $HOME/.firejail directory. Overlays created with --overlay-path=path | ||
1084 | outside $HOME/.firejail will not be deleted. | ||
1085 | .br | ||
1086 | |||
1087 | .br | ||
1088 | Example: | ||
1089 | .br | ||
1090 | $ firejail \-\-overlay-clean | ||
1091 | |||
1092 | .TP | ||
980 | \fB\-\-private | 1093 | \fB\-\-private |
981 | Mount new /root and /home/user directories in temporary | 1094 | Mount new /root and /home/user directories in temporary |
982 | filesystems. All modifications are discarded when the sandbox is | 1095 | filesystems. All modifications are discarded when the sandbox is |
@@ -998,9 +1111,24 @@ Example: | |||
998 | $ firejail \-\-private=/home/netblue/firefox-home firefox | 1111 | $ firejail \-\-private=/home/netblue/firefox-home firefox |
999 | 1112 | ||
1000 | .TP | 1113 | .TP |
1114 | \fB\-\-private-home=file,directory | ||
1115 | Build a new user home in a temporary | ||
1116 | filesystem, and copy the files and directories in the list in the | ||
1117 | new home. All modifications are discarded when the sandbox is | ||
1118 | closed. | ||
1119 | .br | ||
1120 | |||
1121 | .br | ||
1122 | Example: | ||
1123 | .br | ||
1124 | $ firejail \-\-private-home=.mozilla firefox | ||
1125 | |||
1126 | .TP | ||
1001 | \fB\-\-private-bin=file,file | 1127 | \fB\-\-private-bin=file,file |
1002 | Build a new /bin in a temporary filesystem, and copy the programs in the list. | 1128 | Build a new /bin in a temporary filesystem, and copy the programs in the list. |
1129 | If no listed file is found, /bin directory will be empty. | ||
1003 | The same directory is also bind-mounted over /sbin, /usr/bin, /usr/sbin and /usr/local/bin. | 1130 | The same directory is also bind-mounted over /sbin, /usr/bin, /usr/sbin and /usr/local/bin. |
1131 | All modifications are discarded when the sandbox is closed. | ||
1004 | .br | 1132 | .br |
1005 | 1133 | ||
1006 | .br | 1134 | .br |
@@ -1018,7 +1146,7 @@ bash cat ls sed | |||
1018 | 1146 | ||
1019 | .TP | 1147 | .TP |
1020 | \fB\-\-private-dev | 1148 | \fB\-\-private-dev |
1021 | Create a new /dev directory. Only dri, null, full, zero, tty, pts, ptmx, random, urandom, log and shm devices are available. | 1149 | Create a new /dev directory. Only dri, null, full, zero, tty, pts, ptmx, random, snd, urandom, log and shm devices are available. |
1022 | .br | 1150 | .br |
1023 | 1151 | ||
1024 | .br | 1152 | .br |
@@ -1032,14 +1160,15 @@ Child process initialized | |||
1032 | .br | 1160 | .br |
1033 | $ ls /dev | 1161 | $ ls /dev |
1034 | .br | 1162 | .br |
1035 | dri full log null ptmx pts random shm tty urandom zero | 1163 | dri full log null ptmx pts random shm snd tty urandom zero |
1036 | .br | 1164 | .br |
1037 | $ | 1165 | $ |
1038 | .TP | 1166 | .TP |
1039 | \fB\-\-private-etc=file,directory | 1167 | \fB\-\-private-etc=file,directory |
1040 | Build a new /etc in a temporary | 1168 | Build a new /etc in a temporary |
1041 | filesystem, and copy the files and directories in the list. | 1169 | filesystem, and copy the files and directories in the list. |
1042 | All modifications are discarded when the sandbox is closed. | 1170 | If no listed file is found, /etc directory will be empty. |
1171 | All modifications are discarded when the sandbox is closed. | ||
1043 | .br | 1172 | .br |
1044 | 1173 | ||
1045 | .br | 1174 | .br |
@@ -1051,7 +1180,7 @@ nsswitch.conf,passwd,resolv.conf | |||
1051 | 1180 | ||
1052 | .TP | 1181 | .TP |
1053 | \fB\-\-private-tmp | 1182 | \fB\-\-private-tmp |
1054 | Mount an empty temporary filesystem on top of /tmp directory. | 1183 | Mount an empty temporary filesystem on top of /tmp directory whitelisting /tmp/.X11-unix. |
1055 | .br | 1184 | .br |
1056 | 1185 | ||
1057 | .br | 1186 | .br |
@@ -1120,6 +1249,9 @@ $ firejail \-\-protocol.print=3272 | |||
1120 | .br | 1249 | .br |
1121 | unix,inet,inet6,netlink | 1250 | unix,inet,inet6,netlink |
1122 | .TP | 1251 | .TP |
1252 | \fB\-\-put=name|pid src-filename dest-filename | ||
1253 | Put a file in sandbox container, see \fBFILE TRANSFER\fR section for more details. | ||
1254 | .TP | ||
1123 | \fB\-\-quiet | 1255 | \fB\-\-quiet |
1124 | Turn off Firejail's output. | 1256 | Turn off Firejail's output. |
1125 | .TP | 1257 | .TP |
@@ -1131,6 +1263,31 @@ Set directory or file read-only. | |||
1131 | Example: | 1263 | Example: |
1132 | .br | 1264 | .br |
1133 | $ firejail \-\-read-only=~/.mozilla firefox | 1265 | $ firejail \-\-read-only=~/.mozilla firefox |
1266 | .br | ||
1267 | |||
1268 | .br | ||
1269 | A short note about mixing \-\-whitelist and \-\-read-only options. Whitelisted directories | ||
1270 | should be made read-only independently. Making a parent directory read-only, will not | ||
1271 | make the whitelist read-only. Example: | ||
1272 | .br | ||
1273 | |||
1274 | .br | ||
1275 | $ firejail --whitelist=~/work --read-only=~ --read-only=~/work | ||
1276 | |||
1277 | .TP | ||
1278 | \fB\-\-read-write=dirname_or_filename | ||
1279 | Set directory or file read-write. Only files or directories belonging to the current user are allowed for | ||
1280 | this operation. Example: | ||
1281 | .br | ||
1282 | |||
1283 | .br | ||
1284 | $ mkdir ~/test | ||
1285 | .br | ||
1286 | $ touch ~/test/a | ||
1287 | .br | ||
1288 | $ firejail --read-only=~/test --read-write=~/test/a | ||
1289 | |||
1290 | |||
1134 | .TP | 1291 | .TP |
1135 | \fB\-\-rlimit-fsize=number | 1292 | \fB\-\-rlimit-fsize=number |
1136 | Set the maximum file size that can be created by a process. | 1293 | Set the maximum file size that can be created by a process. |
@@ -1143,6 +1300,17 @@ Set the maximum number of processes that can be created for the real user ID of | |||
1143 | .TP | 1300 | .TP |
1144 | \fB\-\-rlimit-sigpending=number | 1301 | \fB\-\-rlimit-sigpending=number |
1145 | Set the maximum number of pending signals for a process. | 1302 | Set the maximum number of pending signals for a process. |
1303 | |||
1304 | .TP | ||
1305 | \fB\-\-rmenv=name | ||
1306 | Remove environment variable in the new sandbox. | ||
1307 | .br | ||
1308 | |||
1309 | .br | ||
1310 | Example: | ||
1311 | .br | ||
1312 | $ firejail \-\-rmenv=DBUS_SESSION_BUS_ADDRESS | ||
1313 | |||
1146 | .TP | 1314 | .TP |
1147 | \fB\-\-scan | 1315 | \fB\-\-scan |
1148 | ARP-scan all the networks from inside a network namespace. | 1316 | ARP-scan all the networks from inside a network namespace. |
@@ -1156,13 +1324,13 @@ $ firejail \-\-net=eth0 \-\-scan | |||
1156 | .TP | 1324 | .TP |
1157 | \fB\-\-seccomp | 1325 | \fB\-\-seccomp |
1158 | Enable seccomp filter and blacklist the syscalls in the default list. The default list is as follows: | 1326 | Enable seccomp filter and blacklist the syscalls in the default list. The default list is as follows: |
1159 | mount, umount2, ptrace, kexec_load, kexec_file_load, open_by_handle_at, init_module, finit_module, delete_module, | 1327 | mount, umount2, ptrace, kexec_load, kexec_file_load, name_to_handle_at, open_by_handle_at, create_module, init_module, finit_module, delete_module, |
1160 | iopl, ioperm, swapon, swapoff, syslog, process_vm_readv, process_vm_writev, | 1328 | iopl, ioperm, ioprio_set, swapon, swapoff, syslog, process_vm_readv, process_vm_writev, |
1161 | sysfs,_sysctl, adjtimex, clock_adjtime, lookup_dcookie, perf_event_open, fanotify_init, kcmp, | 1329 | sysfs,_sysctl, adjtimex, clock_adjtime, lookup_dcookie, perf_event_open, fanotify_init, kcmp, |
1162 | add_key, request_key, keyctl, uselib, acct, modify_ldt, pivot_root, io_setup, | 1330 | add_key, request_key, keyctl, uselib, acct, modify_ldt, pivot_root, io_setup, |
1163 | io_destroy, io_getevents, io_submit, io_cancel, | 1331 | io_destroy, io_getevents, io_submit, io_cancel, |
1164 | remap_file_pages, mbind, get_mempolicy, set_mempolicy, | 1332 | remap_file_pages, mbind, get_mempolicy, set_mempolicy, |
1165 | migrate_pages, move_pages, vmsplice, perf_event_open, chroot, | 1333 | migrate_pages, move_pages, vmsplice, chroot, |
1166 | tuxcall, reboot, mfsservctl and get_kernel_syms. | 1334 | tuxcall, reboot, mfsservctl and get_kernel_syms. |
1167 | .br | 1335 | .br |
1168 | 1336 | ||
@@ -1425,15 +1593,7 @@ $ firejail \-\-tree | |||
1425 | 11969:netblue:firejail \-\-net=eth0 transmission-gtk | 1593 | 11969:netblue:firejail \-\-net=eth0 transmission-gtk |
1426 | .br | 1594 | .br |
1427 | 11970:netblue:transmission-gtk | 1595 | 11970:netblue:transmission-gtk |
1428 | .TP | ||
1429 | \fB\-\-user=new-user | ||
1430 | Switch the user before starting the sandbox. This command should be run as root. | ||
1431 | .br | ||
1432 | 1596 | ||
1433 | .br | ||
1434 | Example: | ||
1435 | .br | ||
1436 | # firejail \-\-user=www-data | ||
1437 | .TP | 1597 | .TP |
1438 | \fB\-\-version | 1598 | \fB\-\-version |
1439 | Print program version and exit. | 1599 | Print program version and exit. |
@@ -1445,66 +1605,106 @@ Example: | |||
1445 | $ firejail \-\-version | 1605 | $ firejail \-\-version |
1446 | .br | 1606 | .br |
1447 | firejail version 0.9.27 | 1607 | firejail version 0.9.27 |
1608 | |||
1609 | .TP | ||
1610 | \fB\-\-veth-name=name | ||
1611 | Use this name for the interface connected to the bridge for --net=bridge_interface commands, | ||
1612 | instead of the default one. | ||
1613 | .br | ||
1614 | |||
1615 | .br | ||
1616 | Example: | ||
1617 | .br | ||
1618 | $ firejail \-\-net=br0 --veth-name=if0 | ||
1619 | |||
1448 | .TP | 1620 | .TP |
1449 | \fB\-\-whitelist=dirname_or_filename | 1621 | \fB\-\-whitelist=dirname_or_filename |
1450 | Whitelist directory or file. This feature is implemented only for user home, /dev, /media, /opt, /var, and /tmp directories. | 1622 | Whitelist directory or file. A temporary file system is mounted on the top directory, and the |
1451 | When whitlisting symbolic links, both the link and the real file should be in the same top directory | 1623 | whitelisted files are mount-binded inside. Modifications to whitelisted files are persistent, |
1452 | (home user, /media, /var etc.) | 1624 | everything else is discarded when the sandbox is closed. The top directory could be |
1625 | user home, /dev, /media, /mnt, /opt, /srv, /var, and /tmp. | ||
1626 | .br | ||
1627 | |||
1628 | .br | ||
1629 | Symbolic link handling: with the exception of user home, both the link and the real file should be in | ||
1630 | the same top directory. For user home, both the link and the real file should be owned by the user. | ||
1453 | .br | 1631 | .br |
1454 | 1632 | ||
1455 | .br | 1633 | .br |
1456 | Example: | 1634 | Example: |
1457 | .br | 1635 | .br |
1458 | $ firejail \-\-whitelist=~/.mozilla \-\-whitelist=~/Downloads | 1636 | $ firejail \-\-noprofile \-\-whitelist=~/.mozilla |
1459 | .br | 1637 | .br |
1460 | $ firejail \-\-whitelist=/tmp/.X11-unix --whitelist=/dev/null | 1638 | $ firejail \-\-whitelist=/tmp/.X11-unix --whitelist=/dev/null |
1461 | .br | 1639 | .br |
1462 | $ firejail "\-\-whitelist=/home/username/My Virtual Machines" | 1640 | $ firejail "\-\-whitelist=/home/username/My Virtual Machines" |
1463 | 1641 | ||
1464 | .TP | 1642 | .TP |
1465 | \fB\-\-x11 | 1643 | \fB\-\-writable-etc |
1466 | Start a new X11 server using Xpra or Xephyr and attach the sandbox to this server. | 1644 | Mount /etc directory read-write. |
1467 | The regular X11 server (display 0) is not visible in the sandbox. This prevents screenshot and keylogger | ||
1468 | applications started in the sandbox from accessing other X11 displays. | ||
1469 | A network namespace needs to be instantiated in order to deny access to X11 abstract Unix domain socket. | ||
1470 | .br | 1645 | .br |
1471 | 1646 | ||
1472 | .br | 1647 | .br |
1473 | Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr. | 1648 | Example: |
1474 | This feature is not available when running as root. | 1649 | .br |
1650 | $ sudo firejail --writable-etc | ||
1651 | |||
1652 | .TP | ||
1653 | \fB\-\-writable-var | ||
1654 | Mount /var directory read-write. | ||
1475 | .br | 1655 | .br |
1476 | 1656 | ||
1477 | .br | 1657 | .br |
1478 | Example: | 1658 | Example: |
1479 | .br | 1659 | .br |
1480 | $ firejail \-\-x11 --net=eth0 firefox | 1660 | $ sudo firejail --writable-var |
1661 | |||
1481 | 1662 | ||
1482 | .TP | 1663 | .TP |
1483 | \fB\-\-x11=xpra | 1664 | \fB\-\-x11 |
1484 | Start a new X11 server using Xpra (http://xpra.org) and attach the sandbox to this server. | 1665 | Sandbox the application using Xpra, Xephyr or Xorg security extension. |
1485 | Xpra is a persistent remote display server and client for forwarding X11 applications and desktop screens. | 1666 | The sandbox will prevents screenshot and keylogger applications started inside the sandbox from accessing |
1486 | On Debian platforms Xpra is installed with the command \fBsudo apt-get install xpra\fR. | 1667 | clients running outside the sandbox. |
1487 | This feature is not available when running as root. | 1668 | Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr. |
1669 | If all fails, Firejail will not attempt to use X11 security extension. | ||
1670 | .br | ||
1671 | |||
1672 | .br | ||
1673 | Xpra and Xephyr modes require a network namespace to be instantiated in order to disable | ||
1674 | X11 abstract Unix socket. If this is not possible, the user can disable the abstract socket | ||
1675 | by adding "-nolisten local" on Xorg command line. | ||
1488 | .br | 1676 | .br |
1489 | 1677 | ||
1490 | .br | 1678 | .br |
1491 | Example: | 1679 | Example: |
1492 | .br | 1680 | .br |
1493 | $ firejail \-\-x11=xpra --net=eth0 firefox | 1681 | $ firejail \-\-x11 --net=eth0 firefox |
1682 | |||
1683 | .TP | ||
1684 | \fB\-\-x11=none | ||
1685 | Blacklist /tmp/.X11-unix directory, ${HOME}/.Xauthority and the file specified in ${XAUTHORITY} environment variable. | ||
1686 | Remove DISPLAY and XAUTHORITY environment variables. | ||
1687 | Stop with error message if X11 abstract socket will be accessible in jail. | ||
1494 | 1688 | ||
1495 | .TP | 1689 | .TP |
1496 | \fB\-\-x11=xephyr | 1690 | \fB\-\-x11=xephyr |
1497 | Start a new X11 server using Xephyr and attach the sandbox to this server. | 1691 | Start Xephyr and attach the sandbox to this server. |
1498 | Xephyr is a display server implementing the X11 display server protocol. | 1692 | Xephyr is a display server implementing the X11 display server protocol. |
1499 | It runs in a window just like other X applications, but it is an X server itself in which you can run other software. | 1693 | A network namespace needs to be instantiated in order to deny access to X11 abstract Unix domain socket. |
1500 | The default Xephyr window size is 800x600. This can be modified in /etc/firejail/firejail.config file, | 1694 | .br |
1501 | see \fBman 5 firejail-config\fR for more details. | 1695 | |
1696 | .br | ||
1697 | Xephyr runs in a window just like any other X11 application. The default window size is 800x600. | ||
1698 | This can be modified in /etc/firejail/firejail.config file. | ||
1502 | .br | 1699 | .br |
1503 | 1700 | ||
1504 | .br | 1701 | .br |
1505 | The recommended way to use this feature is to run a window manager inside the sandbox. | 1702 | The recommended way to use this feature is to run a window manager inside the sandbox. |
1506 | A security profile for OpenBox is provided. | 1703 | A security profile for OpenBox is provided. |
1507 | On Debian platforms Xephyr is installed with the command \fBsudo apt-get install xserver-xephyr\fR. | 1704 | .br |
1705 | |||
1706 | .br | ||
1707 | Xephyr is developed by Xorg project. On Debian platforms it is installed with the command \fBsudo apt-get install xserver-xephyr\fR. | ||
1508 | This feature is not available when running as root. | 1708 | This feature is not available when running as root. |
1509 | .br | 1709 | .br |
1510 | 1710 | ||
@@ -1514,6 +1714,42 @@ Example: | |||
1514 | $ firejail \-\-x11=xephyr --net=eth0 openbox | 1714 | $ firejail \-\-x11=xephyr --net=eth0 openbox |
1515 | 1715 | ||
1516 | .TP | 1716 | .TP |
1717 | \fB\-\-x11=xorg | ||
1718 | Sandbox the application using the untrusted mode implemented by X11 security extension. | ||
1719 | The extension is available in Xorg package | ||
1720 | and it is installed by default on most Linux distributions. It provides support for a simple trusted/untrusted | ||
1721 | connection model. Untrusted clients are restricted in certain ways to prevent them from reading window | ||
1722 | contents of other clients, stealing input events, etc. | ||
1723 | |||
1724 | The untrusted mode has several limitations. A lot of regular programs assume they are a trusted X11 clients | ||
1725 | and will crash or lock up when run in untrusted mode. Chromium browser and xterm are two examples. | ||
1726 | Firefox and transmission-gtk seem to be working fine. | ||
1727 | A network namespace is not required for this option. | ||
1728 | .br | ||
1729 | |||
1730 | .br | ||
1731 | Example: | ||
1732 | .br | ||
1733 | $ firejail \-\-x11=xorg firefox | ||
1734 | |||
1735 | .TP | ||
1736 | \fB\-\-x11=xpra | ||
1737 | Start Xpra (http://xpra.org) and attach the sandbox to this server. | ||
1738 | Xpra is a persistent remote display server and client for forwarding X11 applications and desktop screens. | ||
1739 | A network namespace needs to be instantiated in order to deny access to X11 abstract Unix domain socket. | ||
1740 | .br | ||
1741 | |||
1742 | .br | ||
1743 | On Debian platforms Xpra is installed with the command \fBsudo apt-get install xpra\fR. | ||
1744 | This feature is not available when running as root. | ||
1745 | .br | ||
1746 | |||
1747 | .br | ||
1748 | Example: | ||
1749 | .br | ||
1750 | $ firejail \-\-x11=xpra --net=eth0 firefox | ||
1751 | |||
1752 | .TP | ||
1517 | \fB\-\-zsh | 1753 | \fB\-\-zsh |
1518 | Use /usr/bin/zsh as default user shell. | 1754 | Use /usr/bin/zsh as default user shell. |
1519 | .br | 1755 | .br |
@@ -1576,6 +1812,44 @@ $ firejail --tree | |||
1576 | 1221:netblue:/usr/lib/firefox/firefox | 1812 | 1221:netblue:/usr/lib/firefox/firefox |
1577 | .RE | 1813 | .RE |
1578 | 1814 | ||
1815 | .SH APPARMOR | ||
1816 | .TP | ||
1817 | AppArmor support is disabled by default at compile time. Use --enable-apparmor configuration option to enable it: | ||
1818 | .br | ||
1819 | |||
1820 | .br | ||
1821 | $ ./configure --prefix=/usr --enable-apparmor | ||
1822 | .TP | ||
1823 | During software install, a generic AppArmor profile file, firejail-default, is placed in /etc/apparmor.d directory. The profile needs to be loaded into the kernel by running the following command as root: | ||
1824 | .br | ||
1825 | |||
1826 | .br | ||
1827 | # aa-enforce firejail-default | ||
1828 | .TP | ||
1829 | The installed profile tries to replicate some advanced security features inspired by kernel-based Grsecurity: | ||
1830 | .br | ||
1831 | |||
1832 | .br | ||
1833 | - Prevent information leakage in /proc and /sys directories. The resulting filesystem is barely enough for running | ||
1834 | commands such as "top" and "ps aux". | ||
1835 | .br | ||
1836 | |||
1837 | .br | ||
1838 | - Allow running programs only from well-known system paths, such as /bin, /sbin, /usr/bin etc. Running | ||
1839 | programs and scripts from user home or other directories writable by the user is not allowed. | ||
1840 | .br | ||
1841 | |||
1842 | .br | ||
1843 | - Disable D-Bus. D-Bus has long been a huge security hole, and most programs don't use it anyway. | ||
1844 | You should have no problems running Chromium or Firefox. | ||
1845 | |||
1846 | .TP | ||
1847 | To enable AppArmor confinement on top of your current Firejail security features, pass \fB\-\-apparmor\fR flag to Firejail command line. You can also include \fBapparmor\fR command in a Firejail profile file. Example: | ||
1848 | .br | ||
1849 | |||
1850 | .br | ||
1851 | $ firejail --apparmor firefox | ||
1852 | |||
1579 | .SH FILE TRANSFER | 1853 | .SH FILE TRANSFER |
1580 | These features allow the user to inspect the filesystem container of an existing sandbox | 1854 | These features allow the user to inspect the filesystem container of an existing sandbox |
1581 | and transfer files from the container to the host filesystem. | 1855 | and transfer files from the container to the host filesystem. |
@@ -1583,12 +1857,16 @@ and transfer files from the container to the host filesystem. | |||
1583 | .TP | 1857 | .TP |
1584 | \fB\-\-get=name|pid filename | 1858 | \fB\-\-get=name|pid filename |
1585 | Retrieve the container file and store it on the host in the current working directory. | 1859 | Retrieve the container file and store it on the host in the current working directory. |
1586 | The container is specified by name or PID. Full path is needed for filename. | 1860 | The container is specified by name or PID. |
1587 | 1861 | ||
1588 | .TP | 1862 | .TP |
1589 | \fB\-\-ls=name|pid dir_or_filename | 1863 | \fB\-\-ls=name|pid dir_or_filename |
1590 | List container files. The container is specified by name or PID. | 1864 | List container files. The container is specified by name or PID. |
1591 | Full path is needed for dir_or_filename. | 1865 | |
1866 | .TP | ||
1867 | \fB\-\-put=name|pid src-filename dest-filename | ||
1868 | Put src-filename in sandbox container. | ||
1869 | The container is specified by name or PID. | ||
1592 | 1870 | ||
1593 | .TP | 1871 | .TP |
1594 | Examples: | 1872 | Examples: |
@@ -1614,7 +1892,11 @@ drwxr-xr-x netblue netblue 4096 .. | |||
1614 | 1892 | ||
1615 | .br | 1893 | .br |
1616 | $ firejail \-\-get=mybrowser ~/Downloads/xpra-clipboard.png | 1894 | $ firejail \-\-get=mybrowser ~/Downloads/xpra-clipboard.png |
1895 | .br | ||
1617 | 1896 | ||
1897 | .br | ||
1898 | $ firejail \-\-put=mybrowser xpra-clipboard.png ~/Downloads/xpra-clipboard.png | ||
1899 | .br | ||
1618 | 1900 | ||
1619 | .SH TRAFFIC SHAPING | 1901 | .SH TRAFFIC SHAPING |
1620 | Network bandwidth is an expensive resource shared among all sandboxes running on a system. | 1902 | Network bandwidth is an expensive resource shared among all sandboxes running on a system. |
@@ -1626,15 +1908,15 @@ The shaper works at sandbox level, and can be used only for sandboxes configured | |||
1626 | 1908 | ||
1627 | Set rate-limits: | 1909 | Set rate-limits: |
1628 | 1910 | ||
1629 | firejail --bandwidth=name|pid set network download upload | 1911 | $ firejail --bandwidth=name|pid set network download upload |
1630 | 1912 | ||
1631 | Clear rate-limits: | 1913 | Clear rate-limits: |
1632 | 1914 | ||
1633 | firejail --bandwidth=name|pid clear network | 1915 | $ firejail --bandwidth=name|pid clear network |
1634 | 1916 | ||
1635 | Status: | 1917 | Status: |
1636 | 1918 | ||
1637 | firejail --bandwidth=name|pid status | 1919 | $ firejail --bandwidth=name|pid status |
1638 | 1920 | ||
1639 | where: | 1921 | where: |
1640 | .br | 1922 | .br |
@@ -1658,6 +1940,26 @@ Example: | |||
1658 | .br | 1940 | .br |
1659 | $ firejail \-\-bandwidth=mybrowser clear eth0 | 1941 | $ firejail \-\-bandwidth=mybrowser clear eth0 |
1660 | 1942 | ||
1943 | .SH AUDIT | ||
1944 | Audit feature allows the user to point out gaps in security profiles. The | ||
1945 | implementation replaces the program to be sandboxed with a test program. By | ||
1946 | default, we use faudit program distributed with Firejail. A custom test program | ||
1947 | can also be supplied by the user. Examples: | ||
1948 | |||
1949 | Running the default audit program: | ||
1950 | .br | ||
1951 | $ firejail --audit transmission-gtk | ||
1952 | |||
1953 | Running a custom audit program: | ||
1954 | .br | ||
1955 | $ firejail --audit=~/sandbox-test transmission-gtk | ||
1956 | |||
1957 | In the examples above, the sandbox configures transmission-gtk profile and | ||
1958 | starts the test program. The real program, transmission-gtk, will not be | ||
1959 | started. | ||
1960 | |||
1961 | Limitations: audit feature is not implemented for --x11 commands. | ||
1962 | |||
1661 | .SH MONITORING | 1963 | .SH MONITORING |
1662 | Option \-\-list prints a list of all sandboxes. The format | 1964 | Option \-\-list prints a list of all sandboxes. The format |
1663 | for each process entry is as follows: | 1965 | for each process entry is as follows: |
@@ -1751,7 +2053,7 @@ To disable default profile loading, use --noprofile command option. Example: | |||
1751 | .RS | 2053 | .RS |
1752 | $ firejail | 2054 | $ firejail |
1753 | .br | 2055 | .br |
1754 | Reading profile /etc/firejail/generic.profile | 2056 | Reading profile /etc/firejail/default.profile |
1755 | .br | 2057 | .br |
1756 | Parent pid 8553, child pid 8554 | 2058 | Parent pid 8553, child pid 8554 |
1757 | .br | 2059 | .br |
@@ -1818,7 +2120,6 @@ Homepage: http://firejail.wordpress.com | |||
1818 | \&\flfirecfg\fR\|(1), | 2120 | \&\flfirecfg\fR\|(1), |
1819 | \&\flfirejail-profile\fR\|(5), | 2121 | \&\flfirejail-profile\fR\|(5), |
1820 | \&\flfirejail-login\fR\|(5) | 2122 | \&\flfirejail-login\fR\|(5) |
1821 | \&\flfirejail-config\fR\|(5) | ||
1822 | 2123 | ||
1823 | 2124 | ||
1824 | 2125 | ||
diff --git a/src/man/firemon.txt b/src/man/firemon.txt index ef99b0927..bd84401af 100644 --- a/src/man/firemon.txt +++ b/src/man/firemon.txt | |||
@@ -109,6 +109,5 @@ Homepage: http://firejail.wordpress.com | |||
109 | \&\flfirecfg\fR\|(1), | 109 | \&\flfirecfg\fR\|(1), |
110 | \&\flfirejail-profile\fR\|(5), | 110 | \&\flfirejail-profile\fR\|(5), |
111 | \&\flfirejail-login\fR\|(5) | 111 | \&\flfirejail-login\fR\|(5) |
112 | \&\flfirejail-config\fR\|(5) | ||
113 | 112 | ||
114 | 113 | ||
diff --git a/src/tools/mkcoverit.sh b/src/tools/mkcoverit.sh index 4af84a7a1..65b06f9fa 100755 --- a/src/tools/mkcoverit.sh +++ b/src/tools/mkcoverit.sh | |||
@@ -1,13 +1,13 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | 2 | ||
3 | # unpack firejail archive | 3 | # unpack firejail archive |
4 | ARCFIREJAIL=`ls *.tar.bz2| grep firejail` | 4 | ARCFIREJAIL=`ls *.tar.xz| grep firejail` |
5 | if [ "$?" -eq 0 ]; | 5 | if [ "$?" -eq 0 ]; |
6 | then | 6 | then |
7 | echo "preparing $ARCFIREJAIL" | 7 | echo "preparing $ARCFIREJAIL" |
8 | DIRFIREJAIL=`basename $ARCFIREJAIL .tar.bz2` | 8 | DIRFIREJAIL=`basename $ARCFIREJAIL .tar.xz` |
9 | rm -fr $DIRFIREJAIL | 9 | rm -fr $DIRFIREJAIL |
10 | tar -xjvf $ARCFIREJAIL | 10 | tar -xJvf $ARCFIREJAIL |
11 | cd $DIRFIREJAIL | 11 | cd $DIRFIREJAIL |
12 | ./configure --prefix=/usr | 12 | ./configure --prefix=/usr |
13 | cd .. | 13 | cd .. |
diff --git a/src/tools/syscall_test b/src/tools/syscall_test deleted file mode 100755 index bf29c5b99..000000000 --- a/src/tools/syscall_test +++ /dev/null | |||
Binary files differ | |||
diff --git a/src/tools/syscall_test.c b/src/tools/syscall_test.c deleted file mode 100644 index b3f43c755..000000000 --- a/src/tools/syscall_test.c +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <stdio.h> | ||
3 | #include <unistd.h> | ||
4 | #include <sys/types.h> | ||
5 | #include <sys/socket.h> | ||
6 | #include <linux/netlink.h> | ||
7 | #include <net/ethernet.h> | ||
8 | #include <sys/mount.h> | ||
9 | |||
10 | int main(int argc, char **argv) { | ||
11 | if (argc != 2) { | ||
12 | printf("Usage: test [sleep|socket|mkdir|mount]\n"); | ||
13 | return 1; | ||
14 | } | ||
15 | |||
16 | if (strcmp(argv[1], "sleep") == 0) { | ||
17 | printf("before sleep\n"); | ||
18 | sleep(1); | ||
19 | printf("after sleep\n"); | ||
20 | } | ||
21 | else if (strcmp(argv[1], "socket") == 0) { | ||
22 | int sock; | ||
23 | |||
24 | printf("testing socket AF_INET\n"); | ||
25 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { | ||
26 | perror("socket"); | ||
27 | } | ||
28 | else | ||
29 | close(sock); | ||
30 | |||
31 | printf("testing socket AF_INET6\n"); | ||
32 | if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0) { | ||
33 | perror("socket"); | ||
34 | } | ||
35 | else | ||
36 | close(sock); | ||
37 | |||
38 | printf("testing socket AF_NETLINK\n"); | ||
39 | if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) { | ||
40 | perror("socket"); | ||
41 | } | ||
42 | else | ||
43 | close(sock); | ||
44 | |||
45 | printf("testing socket AF_UNIX\n"); | ||
46 | if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { | ||
47 | perror("socket"); | ||
48 | } | ||
49 | else | ||
50 | close(sock); | ||
51 | |||
52 | // root needed to be able to handle this | ||
53 | printf("testing socket AF_PACKETX\n"); | ||
54 | if ((sock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP))) < 0) { | ||
55 | perror("socket"); | ||
56 | } | ||
57 | else | ||
58 | close(sock); | ||
59 | printf("after socket\n"); | ||
60 | } | ||
61 | else if (strcmp(argv[1], "mkdir") == 0) { | ||
62 | printf("before mkdir\n"); | ||
63 | mkdir("tmp", 0777); | ||
64 | printf("after mkdir\n"); | ||
65 | } | ||
66 | else if (strcmp(argv[1], "mount") == 0) { | ||
67 | printf("before mount\n"); | ||
68 | if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) { | ||
69 | perror("mount"); | ||
70 | } | ||
71 | printf("after mount\n"); | ||
72 | } | ||
73 | else { | ||
74 | fprintf(stderr, "Error: invalid argument\n"); | ||
75 | return 1; | ||
76 | } | ||
77 | return 0; | ||
78 | } | ||
diff --git a/src/tools/syscall_test32 b/src/tools/syscall_test32 deleted file mode 100755 index 8d72f58c4..000000000 --- a/src/tools/syscall_test32 +++ /dev/null | |||
Binary files differ | |||