diff options
author | 2018-07-27 12:56:41 -0400 | |
---|---|---|
committer | 2018-07-27 12:56:41 -0400 | |
commit | 3cf75fe9a34c0bb579502b106649a1fc58d39f35 (patch) | |
tree | 3c696691a48205e6c73987f562bcc7a80aac69e4 /src | |
parent | compile time marker for LTS code (diff) | |
download | firejail-3cf75fe9a34c0bb579502b106649a1fc58d39f35.tar.gz firejail-3cf75fe9a34c0bb579502b106649a1fc58d39f35.tar.zst firejail-3cf75fe9a34c0bb579502b106649a1fc58d39f35.zip |
phase 1
Diffstat (limited to 'src')
33 files changed, 42 insertions, 4958 deletions
diff --git a/src/faudit/Makefile.in b/src/faudit/Makefile.in deleted file mode 100644 index 26df0fe51..000000000 --- a/src/faudit/Makefile.in +++ /dev/null | |||
@@ -1,14 +0,0 @@ | |||
1 | all: faudit | ||
2 | |||
3 | include ../common.mk | ||
4 | |||
5 | %.o : %.c $(H_FILE_LIST) | ||
6 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ | ||
7 | |||
8 | faudit: $(OBJS) | ||
9 | $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) | ||
10 | |||
11 | clean:; rm -f *.o faudit *.gcov *.gcda *.gcno | ||
12 | |||
13 | distclean: clean | ||
14 | rm -fr Makefile | ||
diff --git a/src/faudit/caps.c b/src/faudit/caps.c deleted file mode 100644 index 46c262c89..000000000 --- a/src/faudit/caps.c +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 | } | ||
diff --git a/src/faudit/dbus.c b/src/faudit/dbus.c deleted file mode 100644 index cb08b9b0b..000000000 --- a/src/faudit/dbus.c +++ /dev/null | |||
@@ -1,92 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 | strncpy(remote.sun_path, sockfile, sizeof(remote.sun_path)); | ||
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 | } | ||
diff --git a/src/faudit/dev.c b/src/faudit/dev.c deleted file mode 100644 index 7bf4b279c..000000000 --- a/src/faudit/dev.c +++ /dev/null | |||
@@ -1,47 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 deleted file mode 100644 index 180121ec1..000000000 --- a/src/faudit/faudit.h +++ /dev/null | |||
@@ -1,68 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 deleted file mode 100644 index 1ba18f2ab..000000000 --- a/src/faudit/files.c +++ /dev/null | |||
@@ -1,75 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 deleted file mode 100644 index d73986843..000000000 --- a/src/faudit/main.c +++ /dev/null | |||
@@ -1,98 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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") == 0) { | ||
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 deleted file mode 100644 index 54eef2b2a..000000000 --- a/src/faudit/network.c +++ /dev/null | |||
@@ -1,101 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 deleted file mode 100644 index 22bb68c1a..000000000 --- a/src/faudit/pid.c +++ /dev/null | |||
@@ -1,99 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 | free(fname); | ||
50 | continue; | ||
51 | } | ||
52 | |||
53 | // read file | ||
54 | char buf[100]; | ||
55 | if (fgets(buf, 10, fp) == NULL) { | ||
56 | fclose(fp); | ||
57 | free(fname); | ||
58 | continue; | ||
59 | } | ||
60 | not_visible = 0; | ||
61 | |||
62 | // clean /n | ||
63 | char *ptr; | ||
64 | if ((ptr = strchr(buf, '\n')) != NULL) | ||
65 | *ptr = '\0'; | ||
66 | |||
67 | // check process name against the kernel list | ||
68 | int j = 0; | ||
69 | while (kern_proc[j] != NULL) { | ||
70 | if (strncmp(buf, kern_proc[j], strlen(kern_proc[j])) == 0) { | ||
71 | fclose(fp); | ||
72 | free(fname); | ||
73 | printf("BAD: Process %d is not running in a PID namespace. ", getpid()); | ||
74 | printf("Are you sure you're running in a sandbox?\n"); | ||
75 | return; | ||
76 | } | ||
77 | j++; | ||
78 | } | ||
79 | |||
80 | fclose(fp); | ||
81 | free(fname); | ||
82 | } | ||
83 | |||
84 | pid_t pid = getpid(); | ||
85 | if (not_visible && pid > 100) | ||
86 | printf("BAD: Process %d is not running in a PID namespace.\n", pid); | ||
87 | else | ||
88 | printf("GOOD: process %d is running in a PID namespace.\n", pid); | ||
89 | |||
90 | // try to guess the type of container/sandbox | ||
91 | char *str = getenv("container"); | ||
92 | if (str) | ||
93 | printf("INFO: container/sandbox %s.\n", str); | ||
94 | else { | ||
95 | str = getenv("SNAP"); | ||
96 | if (str) | ||
97 | printf("INFO: this is a snap package\n"); | ||
98 | } | ||
99 | } | ||
diff --git a/src/faudit/seccomp.c b/src/faudit/seccomp.c deleted file mode 100644 index 85a883618..000000000 --- a/src/faudit/seccomp.c +++ /dev/null | |||
@@ -1,101 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 allowed.\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 deleted file mode 100644 index 3650590f3..000000000 --- a/src/faudit/syscall.c +++ /dev/null | |||
@@ -1,105 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 (argc < 3) | ||
38 | return; | ||
39 | |||
40 | if (strcmp(argv[2], "mount") == 0) { | ||
41 | int rv = mount(NULL, NULL, NULL, 0, NULL); | ||
42 | (void) rv; | ||
43 | printf("\nUGLY: mount syscall permitted.\n"); | ||
44 | } | ||
45 | else if (strcmp(argv[2], "umount2") == 0) { | ||
46 | umount2(NULL, 0); | ||
47 | printf("\nUGLY: umount2 syscall permitted.\n"); | ||
48 | } | ||
49 | else if (strcmp(argv[2], "ptrace") == 0) { | ||
50 | ptrace(0, 0, NULL, NULL); | ||
51 | printf("\nUGLY: ptrace syscall permitted.\n"); | ||
52 | } | ||
53 | else if (strcmp(argv[2], "swapon") == 0) { | ||
54 | swapon(NULL, 0); | ||
55 | printf("\nUGLY: swapon syscall permitted.\n"); | ||
56 | } | ||
57 | else if (strcmp(argv[2], "swapoff") == 0) { | ||
58 | swapoff(NULL); | ||
59 | printf("\nUGLY: swapoff syscall permitted.\n"); | ||
60 | } | ||
61 | else if (strcmp(argv[2], "init_module") == 0) { | ||
62 | init_module(NULL, 0, NULL); | ||
63 | printf("\nUGLY: init_module syscall permitted.\n"); | ||
64 | } | ||
65 | else if (strcmp(argv[2], "delete_module") == 0) { | ||
66 | delete_module(NULL, 0); | ||
67 | printf("\nUGLY: delete_module syscall permitted.\n"); | ||
68 | } | ||
69 | else if (strcmp(argv[2], "chroot") == 0) { | ||
70 | int rv = chroot("/blablabla-57281292"); | ||
71 | (void) rv; | ||
72 | printf("\nUGLY: chroot syscall permitted.\n"); | ||
73 | } | ||
74 | else if (strcmp(argv[2], "pivot_root") == 0) { | ||
75 | pivot_root(NULL, NULL); | ||
76 | printf("\nUGLY: pivot_root syscall permitted.\n"); | ||
77 | } | ||
78 | #if defined(__i386__) || defined(__x86_64__) | ||
79 | else if (strcmp(argv[2], "iopl") == 0) { | ||
80 | iopl(0L); | ||
81 | printf("\nUGLY: iopl syscall permitted.\n"); | ||
82 | } | ||
83 | else if (strcmp(argv[2], "ioperm") == 0) { | ||
84 | ioperm(0, 0, 0); | ||
85 | printf("\nUGLY: ioperm syscall permitted.\n"); | ||
86 | } | ||
87 | #endif | ||
88 | exit(0); | ||
89 | } | ||
90 | |||
91 | void syscall_run(const char *name) { | ||
92 | assert(prog); | ||
93 | |||
94 | pid_t child = fork(); | ||
95 | if (child < 0) | ||
96 | errExit("fork"); | ||
97 | if (child == 0) { | ||
98 | execl(prog, prog, "syscall", name, NULL); | ||
99 | perror("execl"); | ||
100 | _exit(1); | ||
101 | } | ||
102 | |||
103 | // wait for the child to finish | ||
104 | waitpid(child, NULL, 0); | ||
105 | } | ||
diff --git a/src/faudit/x11.c b/src/faudit/x11.c deleted file mode 100644 index bb763b110..000000000 --- a/src/faudit/x11.c +++ /dev/null | |||
@@ -1,63 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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/fbuilder/Makefile.in b/src/fbuilder/Makefile.in deleted file mode 100644 index 7a606c872..000000000 --- a/src/fbuilder/Makefile.in +++ /dev/null | |||
@@ -1,14 +0,0 @@ | |||
1 | all: fbuilder | ||
2 | |||
3 | include ../common.mk | ||
4 | |||
5 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h | ||
6 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ | ||
7 | |||
8 | fbuilder: $(OBJS) | ||
9 | $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) | ||
10 | |||
11 | clean:; rm -f *.o fbuilder *.gcov *.gcda *.gcno | ||
12 | |||
13 | distclean: clean | ||
14 | rm -fr Makefile | ||
diff --git a/src/fbuilder/build_bin.c b/src/fbuilder/build_bin.c deleted file mode 100644 index 1230fb780..000000000 --- a/src/fbuilder/build_bin.c +++ /dev/null | |||
@@ -1,126 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 "fbuilder.h" | ||
21 | |||
22 | static FileDB *bin_out = NULL; | ||
23 | |||
24 | static void process_bin(const char *fname) { | ||
25 | assert(fname); | ||
26 | |||
27 | // process trace file | ||
28 | FILE *fp = fopen(fname, "r"); | ||
29 | if (!fp) { | ||
30 | fprintf(stderr, "Error: cannot open %s\n", fname); | ||
31 | exit(1); | ||
32 | } | ||
33 | |||
34 | char buf[MAX_BUF]; | ||
35 | while (fgets(buf, MAX_BUF, fp)) { | ||
36 | // remove \n | ||
37 | char *ptr = strchr(buf, '\n'); | ||
38 | if (ptr) | ||
39 | *ptr = '\0'; | ||
40 | |||
41 | // parse line: 4:galculator:access /etc/fonts/conf.d:0 | ||
42 | // number followed by : | ||
43 | ptr = buf; | ||
44 | if (!isdigit(*ptr)) | ||
45 | continue; | ||
46 | while (isdigit(*ptr)) | ||
47 | ptr++; | ||
48 | if (*ptr != ':') | ||
49 | continue; | ||
50 | ptr++; | ||
51 | |||
52 | // next : | ||
53 | ptr = strchr(ptr, ':'); | ||
54 | if (!ptr) | ||
55 | continue; | ||
56 | ptr++; | ||
57 | if (strncmp(ptr, "exec ", 5) == 0) | ||
58 | ptr += 5; | ||
59 | else | ||
60 | continue; | ||
61 | if (strncmp(ptr, "/bin/", 5) == 0) | ||
62 | ptr += 5; | ||
63 | else if (strncmp(ptr, "/sbin/", 6) == 0) | ||
64 | ptr += 6; | ||
65 | else if (strncmp(ptr, "/usr/bin/", 9) == 0) | ||
66 | ptr += 9; | ||
67 | else if (strncmp(ptr, "/usr/sbin/", 10) == 0) | ||
68 | ptr += 10; | ||
69 | else if (strncmp(ptr, "/usr/local/bin/", 15) == 0) | ||
70 | ptr += 15; | ||
71 | else if (strncmp(ptr, "/usr/local/sbin/", 16) == 0) | ||
72 | ptr += 16; | ||
73 | else if (strncmp(ptr, "/usr/games/", 11) == 0) | ||
74 | ptr += 12; | ||
75 | else if (strncmp(ptr, "/usr/local/games/", 17) == 0) | ||
76 | ptr += 17; | ||
77 | else | ||
78 | continue; | ||
79 | |||
80 | // end of filename | ||
81 | char *ptr2 = strchr(ptr, ':'); | ||
82 | if (!ptr2) | ||
83 | continue; | ||
84 | *ptr2 = '\0'; | ||
85 | |||
86 | // skip strace | ||
87 | if (strcmp(ptr, "strace") == 0) | ||
88 | continue; | ||
89 | |||
90 | bin_out = filedb_add(bin_out, ptr); | ||
91 | } | ||
92 | |||
93 | fclose(fp); | ||
94 | } | ||
95 | |||
96 | |||
97 | // process fname, fname.1, fname.2, fname.3, fname.4, fname.5 | ||
98 | void build_bin(const char *fname, FILE *fp) { | ||
99 | assert(fname); | ||
100 | |||
101 | // run fname | ||
102 | process_bin(fname); | ||
103 | |||
104 | // run all the rest | ||
105 | struct stat s; | ||
106 | int i; | ||
107 | for (i = 1; i <= 5; i++) { | ||
108 | char *newname; | ||
109 | if (asprintf(&newname, "%s.%d", fname, i) == -1) | ||
110 | errExit("asprintf"); | ||
111 | if (stat(newname, &s) == 0) | ||
112 | process_bin(newname); | ||
113 | free(newname); | ||
114 | } | ||
115 | |||
116 | if (bin_out) { | ||
117 | fprintf(fp, "private-bin "); | ||
118 | FileDB *ptr = bin_out; | ||
119 | while (ptr) { | ||
120 | fprintf(fp, "%s,", ptr->fname); | ||
121 | ptr = ptr->next; | ||
122 | } | ||
123 | fprintf(fp, "\n"); | ||
124 | fprintf(fp, "# private-lib\n"); | ||
125 | } | ||
126 | } | ||
diff --git a/src/fbuilder/build_fs.c b/src/fbuilder/build_fs.c deleted file mode 100644 index 771dc94cb..000000000 --- a/src/fbuilder/build_fs.c +++ /dev/null | |||
@@ -1,317 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 "fbuilder.h" | ||
22 | |||
23 | // common file processing function, using the callback for each line in the file | ||
24 | static void process_file(const char *fname, const char *dir, void (*callback)(char *)) { | ||
25 | assert(fname); | ||
26 | assert(dir); | ||
27 | assert(callback); | ||
28 | |||
29 | int dir_len = strlen(dir); | ||
30 | |||
31 | // process trace file | ||
32 | FILE *fp = fopen(fname, "r"); | ||
33 | if (!fp) { | ||
34 | fprintf(stderr, "Error: cannot open %s\n", fname); | ||
35 | exit(1); | ||
36 | } | ||
37 | |||
38 | char buf[MAX_BUF]; | ||
39 | while (fgets(buf, MAX_BUF, fp)) { | ||
40 | // remove \n | ||
41 | char *ptr = strchr(buf, '\n'); | ||
42 | if (ptr) | ||
43 | *ptr = '\0'; | ||
44 | |||
45 | // parse line: 4:galculator:access /etc/fonts/conf.d:0 | ||
46 | // number followed by : | ||
47 | ptr = buf; | ||
48 | if (!isdigit(*ptr)) | ||
49 | continue; | ||
50 | while (isdigit(*ptr)) | ||
51 | ptr++; | ||
52 | if (*ptr != ':') | ||
53 | continue; | ||
54 | ptr++; | ||
55 | |||
56 | // next : | ||
57 | ptr = strchr(ptr, ':'); | ||
58 | if (!ptr) | ||
59 | continue; | ||
60 | ptr++; | ||
61 | if (strncmp(ptr, "access ", 7) == 0) | ||
62 | ptr += 7; | ||
63 | else if (strncmp(ptr, "fopen ", 6) == 0) | ||
64 | ptr += 6; | ||
65 | else if (strncmp(ptr, "fopen64 ", 8) == 0) | ||
66 | ptr += 8; | ||
67 | else if (strncmp(ptr, "open64 ", 7) == 0) | ||
68 | ptr += 7; | ||
69 | else if (strncmp(ptr, "open ", 5) == 0) | ||
70 | ptr += 5; | ||
71 | else | ||
72 | continue; | ||
73 | if (strncmp(ptr, dir, dir_len) != 0) | ||
74 | continue; | ||
75 | |||
76 | // end of filename | ||
77 | char *ptr2 = strchr(ptr, ':'); | ||
78 | if (!ptr2) | ||
79 | continue; | ||
80 | *ptr2 = '\0'; | ||
81 | |||
82 | callback(ptr); | ||
83 | } | ||
84 | |||
85 | fclose(fp); | ||
86 | } | ||
87 | |||
88 | // process fname, fname.1, fname.2, fname.3, fname.4, fname.5 | ||
89 | static void process_files(const char *fname, const char *dir, void (*callback)(char *)) { | ||
90 | assert(fname); | ||
91 | assert(dir); | ||
92 | assert(callback); | ||
93 | |||
94 | // run fname | ||
95 | process_file(fname, dir, callback); | ||
96 | |||
97 | // run all the rest | ||
98 | struct stat s; | ||
99 | int i; | ||
100 | for (i = 1; i <= 5; i++) { | ||
101 | char *newname; | ||
102 | if (asprintf(&newname, "%s.%d", fname, i) == -1) | ||
103 | errExit("asprintf"); | ||
104 | if (stat(newname, &s) == 0) | ||
105 | process_file(newname, dir, callback); | ||
106 | free(newname); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | //******************************************* | ||
111 | // etc directory | ||
112 | //******************************************* | ||
113 | static FileDB *etc_out = NULL; | ||
114 | |||
115 | static void etc_callback(char *ptr) { | ||
116 | // skip firejail directory | ||
117 | if (strncmp(ptr, "/etc/firejail", 13) == 0) | ||
118 | return; | ||
119 | |||
120 | // add only top files and directories | ||
121 | ptr += 5; // skip "/etc/" | ||
122 | char *end = strchr(ptr, '/'); | ||
123 | if (end) | ||
124 | *end = '\0'; | ||
125 | etc_out = filedb_add(etc_out, ptr); | ||
126 | } | ||
127 | |||
128 | void build_etc(const char *fname, FILE *fp) { | ||
129 | assert(fname); | ||
130 | |||
131 | process_files(fname, "/etc", etc_callback); | ||
132 | |||
133 | fprintf(fp, "private-etc "); | ||
134 | if (etc_out == NULL) | ||
135 | fprintf(fp, "none\n"); | ||
136 | else { | ||
137 | FileDB *ptr = etc_out; | ||
138 | while (ptr) { | ||
139 | fprintf(fp, "%s,", ptr->fname); | ||
140 | ptr = ptr->next; | ||
141 | } | ||
142 | fprintf(fp, "\n"); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | //******************************************* | ||
147 | // var directory | ||
148 | //******************************************* | ||
149 | static FileDB *var_out = NULL; | ||
150 | static void var_callback(char *ptr) { | ||
151 | if (strcmp(ptr, "/var/lib") == 0) | ||
152 | ; | ||
153 | else if (strcmp(ptr, "/var/cache") == 0) | ||
154 | ; | ||
155 | else if (strncmp(ptr, "/var/lib/menu-xdg", 17) == 0) | ||
156 | var_out = filedb_add(var_out, "/var/lib/menu-xdg"); | ||
157 | else if (strncmp(ptr, "/var/cache/fontconfig", 21) == 0) | ||
158 | var_out = filedb_add(var_out, "/var/cache/fontconfig"); | ||
159 | else | ||
160 | var_out = filedb_add(var_out, ptr); | ||
161 | } | ||
162 | |||
163 | void build_var(const char *fname, FILE *fp) { | ||
164 | assert(fname); | ||
165 | |||
166 | process_files(fname, "/var", var_callback); | ||
167 | |||
168 | if (var_out == NULL) | ||
169 | fprintf(fp, "blacklist /var\n"); | ||
170 | else | ||
171 | filedb_print(var_out, "whitelist ", fp); | ||
172 | } | ||
173 | |||
174 | |||
175 | //******************************************* | ||
176 | // usr/share directory | ||
177 | //******************************************* | ||
178 | static FileDB *share_out = NULL; | ||
179 | static void share_callback(char *ptr) { | ||
180 | // extract the directory: | ||
181 | assert(strncmp(ptr, "/usr/share", 10) == 0); | ||
182 | char *p1 = ptr + 10; | ||
183 | if (*p1 != '/') | ||
184 | return; | ||
185 | p1++; | ||
186 | if (*p1 == '/') // double '/' | ||
187 | p1++; | ||
188 | if (*p1 == '\0') | ||
189 | return; | ||
190 | |||
191 | // "/usr/share/bash-completion/bash_completion" becomes "/usr/share/bash-completion" | ||
192 | char *p2 = strchr(p1, '/'); | ||
193 | if (p2) | ||
194 | *p2 = '\0'; | ||
195 | |||
196 | // store the file | ||
197 | share_out = filedb_add(share_out, ptr); | ||
198 | } | ||
199 | |||
200 | void build_share(const char *fname, FILE *fp) { | ||
201 | assert(fname); | ||
202 | |||
203 | process_files(fname, "/usr/share", share_callback); | ||
204 | |||
205 | if (share_out == NULL) | ||
206 | fprintf(fp, "blacklist /usr/share\n"); | ||
207 | else | ||
208 | filedb_print(share_out, "whitelist ", fp); | ||
209 | } | ||
210 | |||
211 | //******************************************* | ||
212 | // tmp directory | ||
213 | //******************************************* | ||
214 | static FileDB *tmp_out = NULL; | ||
215 | static void tmp_callback(char *ptr) { | ||
216 | filedb_add(tmp_out, ptr); | ||
217 | } | ||
218 | |||
219 | void build_tmp(const char *fname, FILE *fp) { | ||
220 | assert(fname); | ||
221 | |||
222 | process_files(fname, "/tmp", tmp_callback); | ||
223 | |||
224 | if (tmp_out == NULL) | ||
225 | fprintf(fp, "private-tmp\n"); | ||
226 | else { | ||
227 | fprintf(fp, "\n"); | ||
228 | fprintf(fp, "# private-tmp\n"); | ||
229 | fprintf(fp, "# File accessed in /tmp directory:\n"); | ||
230 | fprintf(fp, "# "); | ||
231 | FileDB *ptr = tmp_out; | ||
232 | while (ptr) { | ||
233 | fprintf(fp, "%s,", ptr->fname); | ||
234 | ptr = ptr->next; | ||
235 | } | ||
236 | printf("\n"); | ||
237 | } | ||
238 | } | ||
239 | |||
240 | //******************************************* | ||
241 | // dev directory | ||
242 | //******************************************* | ||
243 | static char *dev_skip[] = { | ||
244 | "/dev/zero", | ||
245 | "/dev/null", | ||
246 | "/dev/full", | ||
247 | "/dev/random", | ||
248 | "/dev/urandom", | ||
249 | "/dev/tty", | ||
250 | "/dev/snd", | ||
251 | "/dev/dri", | ||
252 | "/dev/pts", | ||
253 | "/dev/nvidia0", | ||
254 | "/dev/nvidia1", | ||
255 | "/dev/nvidia2", | ||
256 | "/dev/nvidia3", | ||
257 | "/dev/nvidia4", | ||
258 | "/dev/nvidia5", | ||
259 | "/dev/nvidia6", | ||
260 | "/dev/nvidia7", | ||
261 | "/dev/nvidia8", | ||
262 | "/dev/nvidia9", | ||
263 | "/dev/nvidiactl", | ||
264 | "/dev/nvidia-modeset", | ||
265 | "/dev/nvidia-uvm", | ||
266 | "/dev/video0", | ||
267 | "/dev/video1", | ||
268 | "/dev/video2", | ||
269 | "/dev/video3", | ||
270 | "/dev/video4", | ||
271 | "/dev/video5", | ||
272 | "/dev/video6", | ||
273 | "/dev/video7", | ||
274 | "/dev/video8", | ||
275 | "/dev/video9", | ||
276 | "/dev/dvb", | ||
277 | "/dev/sr0", | ||
278 | NULL | ||
279 | }; | ||
280 | |||
281 | static FileDB *dev_out = NULL; | ||
282 | static void dev_callback(char *ptr) { | ||
283 | // skip private-dev devices | ||
284 | int i = 0; | ||
285 | int found = 0; | ||
286 | while (dev_skip[i]) { | ||
287 | if (strcmp(ptr, dev_skip[i]) == 0) { | ||
288 | found = 1; | ||
289 | break; | ||
290 | } | ||
291 | i++; | ||
292 | } | ||
293 | if (!found) | ||
294 | filedb_add(dev_out, ptr); | ||
295 | } | ||
296 | |||
297 | void build_dev(const char *fname, FILE *fp) { | ||
298 | assert(fname); | ||
299 | |||
300 | process_files(fname, "/dev", dev_callback); | ||
301 | |||
302 | if (dev_out == NULL) | ||
303 | fprintf(fp, "private-dev\n"); | ||
304 | else { | ||
305 | fprintf(fp, "\n"); | ||
306 | fprintf(fp, "# private-dev\n"); | ||
307 | fprintf(fp, "# This is the list of devices accessed (on top of regular private-dev devices:\n"); | ||
308 | fprintf(fp, "# "); | ||
309 | FileDB *ptr = dev_out; | ||
310 | while (ptr) { | ||
311 | fprintf(fp, "%s,", ptr->fname); | ||
312 | ptr = ptr->next; | ||
313 | } | ||
314 | fprintf(fp, "\n"); | ||
315 | } | ||
316 | } | ||
317 | |||
diff --git a/src/fbuilder/build_home.c b/src/fbuilder/build_home.c deleted file mode 100644 index 7470a8d10..000000000 --- a/src/fbuilder/build_home.c +++ /dev/null | |||
@@ -1,199 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 "fbuilder.h" | ||
22 | |||
23 | static FileDB *db_skip = NULL; | ||
24 | static FileDB *db_out = NULL; | ||
25 | |||
26 | static void load_whitelist_common(void) { | ||
27 | FILE *fp = fopen("/etc/firejail/whitelist-common.inc", "r"); | ||
28 | if (!fp) { | ||
29 | fprintf(stderr, "Error: cannot open whitelist-common.inc\n"); | ||
30 | exit(1); | ||
31 | } | ||
32 | |||
33 | char buf[MAX_BUF]; | ||
34 | while (fgets(buf, MAX_BUF, fp)) { | ||
35 | if (strncmp(buf, "whitelist ~/", 12) != 0) | ||
36 | continue; | ||
37 | char *fn = buf + 12; | ||
38 | char *ptr = strchr(buf, '\n'); | ||
39 | if (!ptr) | ||
40 | continue; | ||
41 | *ptr = '\0'; | ||
42 | |||
43 | // add the file to skip list | ||
44 | db_skip = filedb_add(db_skip, fn); | ||
45 | } | ||
46 | |||
47 | fclose(fp); | ||
48 | } | ||
49 | |||
50 | void process_home(const char *fname, char *home, int home_len) { | ||
51 | assert(fname); | ||
52 | assert(home); | ||
53 | assert(home_len); | ||
54 | |||
55 | // process trace file | ||
56 | FILE *fp = fopen(fname, "r"); | ||
57 | if (!fp) { | ||
58 | fprintf(stderr, "Error: cannot open %s\n", fname); | ||
59 | exit(1); | ||
60 | } | ||
61 | |||
62 | char buf[MAX_BUF]; | ||
63 | while (fgets(buf, MAX_BUF, fp)) { | ||
64 | // remove \n | ||
65 | char *ptr = strchr(buf, '\n'); | ||
66 | if (ptr) | ||
67 | *ptr = '\0'; | ||
68 | |||
69 | // parse line: 4:galculator:access /etc/fonts/conf.d:0 | ||
70 | // number followed by : | ||
71 | ptr = buf; | ||
72 | if (!isdigit(*ptr)) | ||
73 | continue; | ||
74 | while (isdigit(*ptr)) | ||
75 | ptr++; | ||
76 | if (*ptr != ':') | ||
77 | continue; | ||
78 | ptr++; | ||
79 | |||
80 | // next : | ||
81 | ptr = strchr(ptr, ':'); | ||
82 | if (!ptr) | ||
83 | continue; | ||
84 | ptr++; | ||
85 | if (strncmp(ptr, "access /home", 12) == 0) | ||
86 | ptr += 7; | ||
87 | else if (strncmp(ptr, "fopen /home", 11) == 0) | ||
88 | ptr += 6; | ||
89 | else if (strncmp(ptr, "fopen64 /home", 13) == 0) | ||
90 | ptr += 8; | ||
91 | else if (strncmp(ptr, "open64 /home", 12) == 0) | ||
92 | ptr += 7; | ||
93 | else if (strncmp(ptr, "open /home", 10) == 0) | ||
94 | ptr += 5; | ||
95 | else | ||
96 | continue; | ||
97 | |||
98 | // end of filename | ||
99 | char *ptr2 = strchr(ptr, ':'); | ||
100 | if (!ptr2) | ||
101 | continue; | ||
102 | *ptr2 = '\0'; | ||
103 | |||
104 | // check home directory | ||
105 | if (strncmp(ptr, home, home_len) != 0) | ||
106 | continue; | ||
107 | if (strcmp(ptr, home) == 0) | ||
108 | continue; | ||
109 | ptr += home_len + 1; | ||
110 | |||
111 | // skip files handled automatically by firejail | ||
112 | if (strcmp(ptr, ".Xauthority") == 0 || | ||
113 | strcmp(ptr, ".Xdefaults-debian") == 0 || | ||
114 | strncmp(ptr, ".config/pulse/", 13) == 0 || | ||
115 | strncmp(ptr, ".pulse/", 7) == 0 || | ||
116 | strncmp(ptr, ".bash_hist", 10) == 0 || | ||
117 | strcmp(ptr, ".bashrc") == 0) | ||
118 | continue; | ||
119 | |||
120 | |||
121 | // try to find the relevant directory for this file | ||
122 | char *dir = extract_dir(ptr); | ||
123 | char *toadd = (dir)? dir: ptr; | ||
124 | |||
125 | // skip some dot directories | ||
126 | if (strcmp(toadd, ".config") == 0 || | ||
127 | strcmp(toadd, ".local") == 0 || | ||
128 | strcmp(toadd, ".local/share") == 0 || | ||
129 | strcmp(toadd, ".cache") == 0) { | ||
130 | if (dir) | ||
131 | free(dir); | ||
132 | continue; | ||
133 | } | ||
134 | |||
135 | // clean .cache entries | ||
136 | if (strncmp(toadd, ".cache/", 7) == 0) { | ||
137 | char *ptr2 = toadd + 7; | ||
138 | ptr2 = strchr(ptr2, '/'); | ||
139 | if (ptr2) | ||
140 | *ptr2 = '\0'; | ||
141 | } | ||
142 | |||
143 | // skip files and directories in whitelist-common.inc | ||
144 | if (filedb_find(db_skip, toadd)) { | ||
145 | if (dir) | ||
146 | free(dir); | ||
147 | continue; | ||
148 | } | ||
149 | |||
150 | // add the file to out list | ||
151 | db_out = filedb_add(db_out, toadd); | ||
152 | if (dir) | ||
153 | free(dir); | ||
154 | |||
155 | } | ||
156 | fclose(fp); | ||
157 | } | ||
158 | |||
159 | |||
160 | // process fname, fname.1, fname.2, fname.3, fname.4, fname.5 | ||
161 | void build_home(const char *fname, FILE *fp) { | ||
162 | assert(fname); | ||
163 | |||
164 | // load whitelist common | ||
165 | load_whitelist_common(); | ||
166 | |||
167 | // find user home directory | ||
168 | struct passwd *pw = getpwuid(getuid()); | ||
169 | if (!pw) | ||
170 | errExit("getpwuid"); | ||
171 | char *home = pw->pw_dir; | ||
172 | if (!home) | ||
173 | errExit("getpwuid"); | ||
174 | int home_len = strlen(home); | ||
175 | |||
176 | // run fname | ||
177 | process_home(fname, home, home_len); | ||
178 | |||
179 | // run all the rest | ||
180 | struct stat s; | ||
181 | int i; | ||
182 | for (i = 1; i <= 5; i++) { | ||
183 | char *newname; | ||
184 | if (asprintf(&newname, "%s.%d", fname, i) == -1) | ||
185 | errExit("asprintf"); | ||
186 | if (stat(newname, &s) == 0) | ||
187 | process_home(newname, home, home_len); | ||
188 | free(newname); | ||
189 | } | ||
190 | |||
191 | // print the out list if any | ||
192 | if (db_out) { | ||
193 | filedb_print(db_out, "whitelist ~/", fp); | ||
194 | fprintf(fp, "include /etc/firejail/whitelist-common.inc\n"); | ||
195 | } | ||
196 | else | ||
197 | fprintf(fp, "private\n"); | ||
198 | |||
199 | } \ No newline at end of file | ||
diff --git a/src/fbuilder/build_profile.c b/src/fbuilder/build_profile.c deleted file mode 100644 index 125487c41..000000000 --- a/src/fbuilder/build_profile.c +++ /dev/null | |||
@@ -1,170 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 "fbuilder.h" | ||
22 | #include <sys/wait.h> | ||
23 | #include <fcntl.h> | ||
24 | |||
25 | #define TRACE_OUTPUT "/tmp/firejail-trace" | ||
26 | #define STRACE_OUTPUT "/tmp/firejail-strace" | ||
27 | |||
28 | static char *cmdlist[] = { | ||
29 | "/usr/bin/firejail", | ||
30 | "--quiet", | ||
31 | "--output=" TRACE_OUTPUT, | ||
32 | "--noprofile", | ||
33 | "--caps.drop=all", | ||
34 | "--nonewprivs", | ||
35 | "--trace", | ||
36 | "--shell=none", | ||
37 | "/usr/bin/strace", // also used as a marker in build_profile() | ||
38 | "-c", | ||
39 | "-f", | ||
40 | "-o" STRACE_OUTPUT, | ||
41 | }; | ||
42 | |||
43 | static void clear_tmp_files(void) { | ||
44 | unlink(STRACE_OUTPUT); | ||
45 | unlink(TRACE_OUTPUT); | ||
46 | |||
47 | // run all the rest | ||
48 | int i; | ||
49 | for (i = 1; i <= 5; i++) { | ||
50 | char *newname; | ||
51 | if (asprintf(&newname, "%s.%d", TRACE_OUTPUT, i) == -1) | ||
52 | errExit("asprintf"); | ||
53 | unlink(newname); | ||
54 | free(newname); | ||
55 | } | ||
56 | |||
57 | } | ||
58 | |||
59 | void build_profile(int argc, char **argv, int index, FILE *fp) { | ||
60 | // next index is the application name | ||
61 | if (index >= argc) { | ||
62 | fprintf(stderr, "Error: application name missing\n"); | ||
63 | exit(1); | ||
64 | } | ||
65 | |||
66 | // clean /tmp files | ||
67 | clear_tmp_files(); | ||
68 | |||
69 | // detect strace | ||
70 | int have_strace = 0; | ||
71 | if (access("/usr/bin/strace", X_OK) == 0) | ||
72 | have_strace = 1; | ||
73 | |||
74 | // calculate command length | ||
75 | unsigned len = (int) sizeof(cmdlist) / sizeof(char*) + argc - index + 1; | ||
76 | if (arg_debug) | ||
77 | printf("command len %d + %d + 1\n", (int) (sizeof(cmdlist) / sizeof(char*)), argc - index); | ||
78 | char *cmd[len]; | ||
79 | cmd[0] = cmdlist[0]; // explicit assignemnt to clean scan-build error | ||
80 | |||
81 | // build command | ||
82 | unsigned i = 0; | ||
83 | for (i = 0; i < (int) sizeof(cmdlist) / sizeof(char*); i++) { | ||
84 | // skip strace if not installed | ||
85 | if (have_strace == 0 && strcmp(cmdlist[i], "/usr/bin/strace") == 0) | ||
86 | break; | ||
87 | cmd[i] = cmdlist[i]; | ||
88 | } | ||
89 | |||
90 | int i2 = index; | ||
91 | for (; i < (len - 1); i++, i2++) | ||
92 | cmd[i] = argv[i2]; | ||
93 | assert(i < len); | ||
94 | cmd[i] = NULL; | ||
95 | |||
96 | if (arg_debug) { | ||
97 | for (i = 0; i < len; i++) | ||
98 | printf("\t%s\n", cmd[i]); | ||
99 | } | ||
100 | |||
101 | // fork and execute | ||
102 | pid_t child = fork(); | ||
103 | if (child == -1) | ||
104 | errExit("fork"); | ||
105 | if (child == 0) { | ||
106 | assert(cmd[0]); | ||
107 | int rv = execvp(cmd[0], cmd); | ||
108 | (void) rv; | ||
109 | errExit("execv"); | ||
110 | } | ||
111 | |||
112 | // wait for all processes to finish | ||
113 | int status; | ||
114 | if (waitpid(child, &status, 0) != child) | ||
115 | errExit("waitpid"); | ||
116 | |||
117 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { | ||
118 | printf("\n\n\n"); | ||
119 | fprintf(fp, "############################################\n"); | ||
120 | fprintf(fp, "# %s profile\n", argv[index]); | ||
121 | fprintf(fp, "############################################\n"); | ||
122 | fprintf(fp, "# Persistent global definitions\n"); | ||
123 | fprintf(fp, "# include /etc/firejail/globals.local\n"); | ||
124 | fprintf(fp, "\n"); | ||
125 | |||
126 | fprintf(fp, "### basic blacklisting\n"); | ||
127 | fprintf(fp, "include /etc/firejail/disable-common.inc\n"); | ||
128 | fprintf(fp, "# include /etc/firejail/disable-devel.inc\n"); | ||
129 | fprintf(fp, "include /etc/firejail/disable-passwdmgr.inc\n"); | ||
130 | fprintf(fp, "# include /etc/firejail/disable-programs.inc\n"); | ||
131 | fprintf(fp, "\n"); | ||
132 | |||
133 | fprintf(fp, "### home directory whitelisting\n"); | ||
134 | build_home(TRACE_OUTPUT, fp); | ||
135 | fprintf(fp, "\n"); | ||
136 | |||
137 | fprintf(fp, "### filesystem\n"); | ||
138 | build_tmp(TRACE_OUTPUT, fp); | ||
139 | build_dev(TRACE_OUTPUT, fp); | ||
140 | build_etc(TRACE_OUTPUT, fp); | ||
141 | build_var(TRACE_OUTPUT, fp); | ||
142 | build_bin(TRACE_OUTPUT, fp); | ||
143 | build_share(TRACE_OUTPUT, fp); | ||
144 | fprintf(fp, "\n"); | ||
145 | |||
146 | fprintf(fp, "### security filters\n"); | ||
147 | fprintf(fp, "caps.drop all\n"); | ||
148 | fprintf(fp, "nonewprivs\n"); | ||
149 | fprintf(fp, "seccomp\n"); | ||
150 | if (have_strace) | ||
151 | build_seccomp(STRACE_OUTPUT, fp); | ||
152 | else { | ||
153 | fprintf(fp, "# If you install strace on your system, Firejail will also create a\n"); | ||
154 | fprintf(fp, "# whitelisted seccomp filter.\n"); | ||
155 | } | ||
156 | fprintf(fp, "\n"); | ||
157 | |||
158 | fprintf(fp, "### network\n"); | ||
159 | build_protocol(TRACE_OUTPUT, fp); | ||
160 | fprintf(fp, "\n"); | ||
161 | |||
162 | fprintf(fp, "### environment\n"); | ||
163 | fprintf(fp, "shell none\n"); | ||
164 | |||
165 | } | ||
166 | else { | ||
167 | fprintf(stderr, "Error: cannot run the sandbox\n"); | ||
168 | exit(1); | ||
169 | } | ||
170 | } | ||
diff --git a/src/fbuilder/build_seccomp.c b/src/fbuilder/build_seccomp.c deleted file mode 100644 index fbc0e06f4..000000000 --- a/src/fbuilder/build_seccomp.c +++ /dev/null | |||
@@ -1,192 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 "fbuilder.h" | ||
22 | |||
23 | void build_seccomp(const char *fname, FILE *fp) { | ||
24 | assert(fname); | ||
25 | assert(fp); | ||
26 | |||
27 | FILE *fp2 = fopen(fname, "r"); | ||
28 | if (!fp2) { | ||
29 | fprintf(stderr, "Error: cannot open %s\n", fname); | ||
30 | exit(1); | ||
31 | } | ||
32 | |||
33 | char buf[MAX_BUF]; | ||
34 | int line = 1; | ||
35 | int position = 0; | ||
36 | int cnt = 0; | ||
37 | while (fgets(buf, MAX_BUF, fp2)) { | ||
38 | // remove \n | ||
39 | char *ptr = strchr(buf, '\n'); | ||
40 | if (ptr) | ||
41 | *ptr = '\0'; | ||
42 | |||
43 | // first line: | ||
44 | //% time seconds usecs/call calls errors syscall | ||
45 | if (line == 1) { | ||
46 | // extract syscall position | ||
47 | ptr = strstr(buf, "syscall"); | ||
48 | if (*buf != '%' || ptr == NULL) { | ||
49 | // skip this line, it could be garbage from strace | ||
50 | continue; | ||
51 | } | ||
52 | position = (int) (ptr - buf); | ||
53 | } | ||
54 | else if (line == 2) { | ||
55 | if (*buf != '-') { | ||
56 | fprintf(stderr, "Error: invalid strace output\n%s\n", buf); | ||
57 | exit(1); | ||
58 | } | ||
59 | } | ||
60 | else { | ||
61 | // get out on the next "----" line | ||
62 | if (*buf == '-') | ||
63 | break; | ||
64 | |||
65 | if (line == 3) | ||
66 | fprintf(fp, "# seccomp.keep %s", buf + position); | ||
67 | else | ||
68 | fprintf(fp, ",%s", buf + position); | ||
69 | cnt++; | ||
70 | } | ||
71 | line++; | ||
72 | } | ||
73 | fprintf(fp, "\n"); | ||
74 | fprintf(fp, "# %d syscalls total\n", cnt); | ||
75 | fprintf(fp, "# Probably you will need to add more syscalls to seccomp.keep. Look for\n"); | ||
76 | fprintf(fp, "# seccomp errors in /var/log/syslog or /var/log/audit/audit.log while\n"); | ||
77 | fprintf(fp, "# running your sandbox.\n"); | ||
78 | |||
79 | fclose(fp2); | ||
80 | } | ||
81 | |||
82 | //*************************************** | ||
83 | // protocol | ||
84 | //*************************************** | ||
85 | int unix_s = 0; | ||
86 | int inet = 0; | ||
87 | int inet6 = 0; | ||
88 | int netlink = 0; | ||
89 | int packet = 0; | ||
90 | static void process_protocol(const char *fname) { | ||
91 | assert(fname); | ||
92 | |||
93 | // process trace file | ||
94 | FILE *fp = fopen(fname, "r"); | ||
95 | if (!fp) { | ||
96 | fprintf(stderr, "Error: cannot open %s\n", fname); | ||
97 | exit(1); | ||
98 | } | ||
99 | |||
100 | char buf[MAX_BUF]; | ||
101 | while (fgets(buf, MAX_BUF, fp)) { | ||
102 | // remove \n | ||
103 | char *ptr = strchr(buf, '\n'); | ||
104 | if (ptr) | ||
105 | *ptr = '\0'; | ||
106 | |||
107 | // parse line: 4:galculator:access /etc/fonts/conf.d:0 | ||
108 | // number followed by : | ||
109 | ptr = buf; | ||
110 | if (!isdigit(*ptr)) | ||
111 | continue; | ||
112 | while (isdigit(*ptr)) | ||
113 | ptr++; | ||
114 | if (*ptr != ':') | ||
115 | continue; | ||
116 | ptr++; | ||
117 | |||
118 | // next : | ||
119 | ptr = strchr(ptr, ':'); | ||
120 | if (!ptr) | ||
121 | continue; | ||
122 | ptr++; | ||
123 | if (strncmp(ptr, "socket ", 7) == 0) | ||
124 | ptr += 7; | ||
125 | else | ||
126 | continue; | ||
127 | |||
128 | if (strncmp(ptr, "AF_LOCAL ", 9) == 0) | ||
129 | unix_s = 1; | ||
130 | else if (strncmp(ptr, "AF_INET ", 8) == 0) | ||
131 | inet = 1; | ||
132 | else if (strncmp(ptr, "AF_INET6 ", 9) == 0) | ||
133 | inet6 = 1; | ||
134 | else if (strncmp(ptr, "AF_NETLINK ", 9) == 0) | ||
135 | netlink = 1; | ||
136 | else if (strncmp(ptr, "AF_PACKET ", 9) == 0) | ||
137 | packet = 1; | ||
138 | } | ||
139 | |||
140 | fclose(fp); | ||
141 | } | ||
142 | |||
143 | |||
144 | // process fname, fname.1, fname.2, fname.3, fname.4, fname.5 | ||
145 | void build_protocol(const char *fname, FILE *fp) { | ||
146 | assert(fname); | ||
147 | |||
148 | // run fname | ||
149 | process_protocol(fname); | ||
150 | |||
151 | // run all the rest | ||
152 | struct stat s; | ||
153 | int i; | ||
154 | for (i = 1; i <= 5; i++) { | ||
155 | char *newname; | ||
156 | if (asprintf(&newname, "%s.%d", fname, i) == -1) | ||
157 | errExit("asprintf"); | ||
158 | if (stat(newname, &s) == 0) | ||
159 | process_protocol(newname); | ||
160 | free(newname); | ||
161 | } | ||
162 | |||
163 | int net = 0; | ||
164 | if (unix_s || inet || inet6 || netlink || packet) { | ||
165 | fprintf(fp, "protocol "); | ||
166 | if (unix_s) | ||
167 | fprintf(fp, "unix,"); | ||
168 | if (inet) { | ||
169 | fprintf(fp, "inet,"); | ||
170 | net = 1; | ||
171 | } | ||
172 | if (inet6) { | ||
173 | fprintf(fp, "inet6,"); | ||
174 | net = 1; | ||
175 | } | ||
176 | if (netlink) | ||
177 | fprintf(fp, "netlink,"); | ||
178 | if (packet) { | ||
179 | fprintf(fp, "packet"); | ||
180 | net = 1; | ||
181 | } | ||
182 | fprintf(fp, "\n"); | ||
183 | } | ||
184 | |||
185 | if (net == 0) | ||
186 | fprintf(fp, "net none\n"); | ||
187 | else { | ||
188 | fprintf(fp, "# net eth0\n"); | ||
189 | fprintf(fp, "netfilter\n"); | ||
190 | } | ||
191 | } | ||
192 | |||
diff --git a/src/fbuilder/fbuilder.h b/src/fbuilder/fbuilder.h deleted file mode 100644 index 0a0fd42c9..000000000 --- a/src/fbuilder/fbuilder.h +++ /dev/null | |||
@@ -1,69 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 FBUILDER_H | ||
22 | #define FBUILDER_H | ||
23 | #include "../include/common.h" | ||
24 | #include <sys/types.h> | ||
25 | #include <pwd.h> | ||
26 | #include <sys/types.h> | ||
27 | #include <sys/stat.h> | ||
28 | |||
29 | |||
30 | #define MAX_BUF 4096 | ||
31 | // main.c | ||
32 | extern int arg_debug; | ||
33 | |||
34 | // build_profile.c | ||
35 | void build_profile(int argc, char **argv, int index, FILE *fp); | ||
36 | |||
37 | // build_seccomp.c | ||
38 | void build_seccomp(const char *fname, FILE *fp); | ||
39 | void build_protocol(const char *fname, FILE *fp); | ||
40 | |||
41 | // build_fs.c | ||
42 | void build_etc(const char *fname, FILE *fp); | ||
43 | void build_var(const char *fname, FILE *fp); | ||
44 | void build_tmp(const char *fname, FILE *fp); | ||
45 | void build_dev(const char *fname, FILE *fp); | ||
46 | void build_share(const char *fname, FILE *fp); | ||
47 | |||
48 | // build_bin.c | ||
49 | void build_bin(const char *fname, FILE *fp); | ||
50 | |||
51 | // build_home.c | ||
52 | void build_home(const char *fname, FILE *fp); | ||
53 | |||
54 | // utils.c | ||
55 | int is_dir(const char *fname); | ||
56 | char *extract_dir(char *fname); | ||
57 | |||
58 | // filedb.c | ||
59 | typedef struct filedb_t { | ||
60 | struct filedb_t *next; | ||
61 | char *fname; // file name | ||
62 | int len; // length of file name | ||
63 | } FileDB; | ||
64 | |||
65 | FileDB *filedb_add(FileDB *head, const char *fname); | ||
66 | FileDB *filedb_find(FileDB *head, const char *fname); | ||
67 | void filedb_print(FileDB *head, const char *prefix, FILE *fp); | ||
68 | |||
69 | #endif \ No newline at end of file | ||
diff --git a/src/fbuilder/filedb.c b/src/fbuilder/filedb.c deleted file mode 100644 index 7af3724e8..000000000 --- a/src/fbuilder/filedb.c +++ /dev/null | |||
@@ -1,79 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 "fbuilder.h" | ||
22 | |||
23 | FileDB *filedb_find(FileDB *head, const char *fname) { | ||
24 | FileDB *ptr = head; | ||
25 | int found = 0; | ||
26 | int len = strlen(fname); | ||
27 | |||
28 | while (ptr) { | ||
29 | // exact name | ||
30 | if (strcmp(fname, ptr->fname) == 0) { | ||
31 | found = 1; | ||
32 | break; | ||
33 | } | ||
34 | |||
35 | // parent directory in the list | ||
36 | if (len > ptr->len && | ||
37 | fname[ptr->len] == '/' && | ||
38 | strncmp(ptr->fname, fname, ptr->len) == 0) { | ||
39 | found = 1; | ||
40 | break; | ||
41 | } | ||
42 | |||
43 | ptr = ptr->next; | ||
44 | } | ||
45 | |||
46 | if (found) | ||
47 | return ptr; | ||
48 | |||
49 | return NULL; | ||
50 | } | ||
51 | |||
52 | FileDB *filedb_add(FileDB *head, const char *fname) { | ||
53 | assert(fname); | ||
54 | |||
55 | // don't add it if it is already there or if the parent directory is already in the list | ||
56 | if (filedb_find(head, fname)) | ||
57 | return head; | ||
58 | |||
59 | // add a new entry | ||
60 | FileDB *entry = malloc(sizeof(FileDB)); | ||
61 | if (!entry) | ||
62 | errExit("malloc"); | ||
63 | memset(entry, 0, sizeof(FileDB)); | ||
64 | entry->fname = strdup(fname); | ||
65 | if (!entry->fname) | ||
66 | errExit("strdup"); | ||
67 | entry->len = strlen(entry->fname); | ||
68 | entry->next = head; | ||
69 | return entry; | ||
70 | }; | ||
71 | |||
72 | void filedb_print(FileDB *head, const char *prefix, FILE *fp) { | ||
73 | FileDB *ptr = head; | ||
74 | while (ptr) { | ||
75 | fprintf(fp, "%s%s\n", prefix, ptr->fname); | ||
76 | ptr = ptr->next; | ||
77 | } | ||
78 | } | ||
79 | |||
diff --git a/src/fbuilder/main.c b/src/fbuilder/main.c deleted file mode 100644 index ef5dee7d9..000000000 --- a/src/fbuilder/main.c +++ /dev/null | |||
@@ -1,93 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 "fbuilder.h" | ||
21 | int arg_debug = 0; | ||
22 | |||
23 | static void usage(void) { | ||
24 | printf("Firejail profile builder\n"); | ||
25 | printf("Usage: firejail [--debug] --build[=profile-file] program-and-arguments\n"); | ||
26 | } | ||
27 | |||
28 | int main(int argc, char **argv) { | ||
29 | #if 0 | ||
30 | { | ||
31 | system("cat /proc/self/status"); | ||
32 | int i; | ||
33 | for (i = 0; i < argc; i++) | ||
34 | printf("*%s* ", argv[i]); | ||
35 | printf("\n"); | ||
36 | } | ||
37 | #endif | ||
38 | |||
39 | int i; | ||
40 | int prog_index = 0; | ||
41 | FILE *fp = stdout; | ||
42 | int prof_file = 0; | ||
43 | |||
44 | // parse arguments and extract program index | ||
45 | for (i = 1; i < argc; i++) { | ||
46 | if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") ==0) { | ||
47 | usage(); | ||
48 | return 0; | ||
49 | } | ||
50 | else if (strcmp(argv[i], "--debug") == 0) | ||
51 | arg_debug = 1; | ||
52 | else if (strcmp(argv[i], "--build") == 0) | ||
53 | ; // do nothing, this is passed down from firejail | ||
54 | else if (strncmp(argv[i], "--build=", 8) == 0) { | ||
55 | // this option is only supported for non-root users | ||
56 | if (getuid() == 0) { | ||
57 | fprintf(stderr, "Error fbuild: --build=profile-name is not supported for root user.\n"); | ||
58 | exit(1); | ||
59 | } | ||
60 | |||
61 | // check file access | ||
62 | fp = fopen(argv[i] + 8, "w"); | ||
63 | if (!fp) { | ||
64 | fprintf(stderr, "Error fbuild: cannot open profile file.\n"); | ||
65 | exit(1); | ||
66 | } | ||
67 | prof_file = 1; | ||
68 | // do nothing, this is passed down from firejail | ||
69 | } | ||
70 | else { | ||
71 | if (*argv[i] == '-') { | ||
72 | fprintf(stderr, "Error fbuilder: invalid program\n"); | ||
73 | usage(); | ||
74 | exit(1); | ||
75 | } | ||
76 | prog_index = i; | ||
77 | break; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | if (prog_index == 0) { | ||
82 | fprintf(stderr, "Error fbuilder: program and arguments required\n"); | ||
83 | usage(); | ||
84 | if (prof_file) | ||
85 | fclose(fp); | ||
86 | exit(1); | ||
87 | } | ||
88 | |||
89 | build_profile(argc, argv, prog_index, fp); | ||
90 | if (prof_file) | ||
91 | fclose(fp); | ||
92 | return 0; | ||
93 | } | ||
diff --git a/src/fbuilder/utils.c b/src/fbuilder/utils.c deleted file mode 100644 index 1d490b07e..000000000 --- a/src/fbuilder/utils.c +++ /dev/null | |||
@@ -1,72 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 "fbuilder.h" | ||
22 | |||
23 | // todo: duplicated from src/firejail/util.c - remove dplication | ||
24 | // return 1 if the file is a directory | ||
25 | int is_dir(const char *fname) { | ||
26 | assert(fname); | ||
27 | if (*fname == '\0') | ||
28 | return 0; | ||
29 | |||
30 | // if fname doesn't end in '/', add one | ||
31 | int rv; | ||
32 | struct stat s; | ||
33 | if (fname[strlen(fname) - 1] == '/') | ||
34 | rv = stat(fname, &s); | ||
35 | else { | ||
36 | char *tmp; | ||
37 | if (asprintf(&tmp, "%s/", fname) == -1) { | ||
38 | fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__); | ||
39 | errExit("asprintf"); | ||
40 | } | ||
41 | rv = stat(tmp, &s); | ||
42 | free(tmp); | ||
43 | } | ||
44 | |||
45 | if (rv == -1) | ||
46 | return 0; | ||
47 | |||
48 | if (S_ISDIR(s.st_mode)) | ||
49 | return 1; | ||
50 | |||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | // return NULL if fname is already a directory, or if no directory found | ||
55 | char *extract_dir(char *fname) { | ||
56 | assert(fname); | ||
57 | if (is_dir(fname)) | ||
58 | return NULL; | ||
59 | |||
60 | char *name = strdup(fname); | ||
61 | if (!name) | ||
62 | errExit("strdup"); | ||
63 | |||
64 | char *ptr = strrchr(name, '/'); | ||
65 | if (!ptr) { | ||
66 | free(name); | ||
67 | return NULL; | ||
68 | } | ||
69 | *ptr = '\0'; | ||
70 | |||
71 | return name; | ||
72 | } | ||
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 24ff553d7..ba2f8e284 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -740,7 +740,7 @@ void fs_basic_fs(void) { | |||
740 | } | 740 | } |
741 | 741 | ||
742 | 742 | ||
743 | 743 | #ifndef LTS | |
744 | #ifdef HAVE_OVERLAYFS | 744 | #ifdef HAVE_OVERLAYFS |
745 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { | 745 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { |
746 | struct stat s; | 746 | struct stat s; |
@@ -1292,6 +1292,7 @@ void fs_chroot(const char *rootdir) { | |||
1292 | disable_config(); | 1292 | disable_config(); |
1293 | } | 1293 | } |
1294 | #endif | 1294 | #endif |
1295 | #endif // LTS | ||
1295 | 1296 | ||
1296 | // this function is called from sandbox.c before blacklist/whitelist functions | 1297 | // this function is called from sandbox.c before blacklist/whitelist functions |
1297 | void fs_private_tmp(void) { | 1298 | void fs_private_tmp(void) { |
diff --git a/src/firejail/fs_bin.c b/src/firejail/fs_bin.c deleted file mode 100644 index 5625ed356..000000000 --- a/src/firejail/fs_bin.c +++ /dev/null | |||
@@ -1,309 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 | #include <sys/types.h> | ||
24 | #include <sys/wait.h> | ||
25 | #include <unistd.h> | ||
26 | #include <glob.h> | ||
27 | |||
28 | static int prog_cnt = 0; | ||
29 | |||
30 | static char *paths[] = { | ||
31 | "/usr/local/bin", | ||
32 | "/usr/bin", | ||
33 | "/bin", | ||
34 | "/usr/games", | ||
35 | "/usr/local/games", | ||
36 | "/usr/local/sbin", | ||
37 | "/usr/sbin", | ||
38 | "/sbin", | ||
39 | NULL | ||
40 | }; | ||
41 | |||
42 | // return 1 if found, 0 if not found | ||
43 | static char *check_dir_or_file(const char *name) { | ||
44 | assert(name); | ||
45 | struct stat s; | ||
46 | char *fname = NULL; | ||
47 | |||
48 | int i = 0; | ||
49 | while (paths[i]) { | ||
50 | // private-bin-no-local can be disabled in /etc/firejail/firejail.config | ||
51 | if (checkcfg(CFG_PRIVATE_BIN_NO_LOCAL) && strstr(paths[i], "local/")) { | ||
52 | i++; | ||
53 | continue; | ||
54 | } | ||
55 | |||
56 | // check file | ||
57 | if (asprintf(&fname, "%s/%s", paths[i], name) == -1) | ||
58 | errExit("asprintf"); | ||
59 | if (arg_debug) | ||
60 | printf("Checking %s/%s\n", paths[i], name); | ||
61 | if (stat(fname, &s) == 0 && !S_ISDIR(s.st_mode)) { // do not allow directories | ||
62 | // check symlink to firejail executable in /usr/local/bin | ||
63 | if (strcmp(paths[i], "/usr/local/bin") == 0 && is_link(fname)) { | ||
64 | /* coverity[toctou] */ | ||
65 | char *actual_path = realpath(fname, NULL); | ||
66 | if (actual_path) { | ||
67 | char *ptr = strstr(actual_path, "/firejail"); | ||
68 | if (ptr && strlen(ptr) == strlen("/firejail")) { | ||
69 | if (arg_debug) | ||
70 | printf("firejail exec symlink detected\n"); | ||
71 | free(actual_path); | ||
72 | free(fname); | ||
73 | fname = NULL; | ||
74 | i++; | ||
75 | continue; | ||
76 | } | ||
77 | free(actual_path); | ||
78 | } | ||
79 | |||
80 | } | ||
81 | break; // file found | ||
82 | } | ||
83 | |||
84 | free(fname); | ||
85 | fname = NULL; | ||
86 | i++; | ||
87 | } | ||
88 | |||
89 | if (!fname) { | ||
90 | if (arg_debug) | ||
91 | fwarning("file %s not found\n", name); | ||
92 | return NULL; | ||
93 | } | ||
94 | |||
95 | free(fname); | ||
96 | return paths[i]; | ||
97 | } | ||
98 | |||
99 | // return 1 if the file is in paths[] | ||
100 | static int valid_full_path_file(const char *name) { | ||
101 | assert(name); | ||
102 | |||
103 | if (*name != '/') | ||
104 | return 0; | ||
105 | if (strstr(name, "..")) | ||
106 | return 0; | ||
107 | |||
108 | // do we have a file? | ||
109 | struct stat s; | ||
110 | if (stat(name, &s) == -1) | ||
111 | return 0; | ||
112 | // directories not allowed | ||
113 | if (S_ISDIR(s.st_mode)) | ||
114 | return 0; | ||
115 | // checking access | ||
116 | if (access(name, X_OK) == -1) | ||
117 | return 0; | ||
118 | |||
119 | // check standard paths | ||
120 | int i = 0; | ||
121 | while (paths[i]) { | ||
122 | // private-bin-no-local can be disabled in /etc/firejail/firejail.config | ||
123 | if (checkcfg(CFG_PRIVATE_BIN_NO_LOCAL) && strstr(paths[i], "local/")) { | ||
124 | i++; | ||
125 | continue; | ||
126 | } | ||
127 | |||
128 | int len = strlen(paths[i]); | ||
129 | if (strncmp(name, paths[i], len) == 0 && name[len] == '/' && name[len + 1] != '\0') | ||
130 | return 1; | ||
131 | i++; | ||
132 | } | ||
133 | if (arg_debug) | ||
134 | printf("file %s not found\n", name); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static void report_duplication(const char *fname) { | ||
139 | // report the file on all bin paths | ||
140 | int i = 0; | ||
141 | while (paths[i]) { | ||
142 | char *p; | ||
143 | if (asprintf(&p, "%s/%s", paths[i], fname) == -1) | ||
144 | errExit("asprintf"); | ||
145 | fs_logger2("clone", p); | ||
146 | free(p); | ||
147 | i++; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | static void duplicate(char *fname) { | ||
152 | assert(fname); | ||
153 | |||
154 | if (*fname == '~' || strstr(fname, "..")) { | ||
155 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); | ||
156 | exit(1); | ||
157 | } | ||
158 | invalid_filename(fname, 0); // no globbing | ||
159 | |||
160 | char *full_path; | ||
161 | if (*fname == '/') { | ||
162 | // If the absolute filename is indicated, directly use it. This | ||
163 | // is required for the following cases: | ||
164 | // - if user's $PATH order is not the same as the above | ||
165 | // paths[] variable order | ||
166 | if (!valid_full_path_file(fname)) { | ||
167 | fwarning("invalid private-bin path %s\n", fname); | ||
168 | return; | ||
169 | } | ||
170 | |||
171 | full_path = strdup(fname); | ||
172 | if (!full_path) | ||
173 | errExit("strdup"); | ||
174 | } | ||
175 | else { | ||
176 | // Find the standard directory (by looping through paths[]) | ||
177 | // where the filename fname is located | ||
178 | char *path = check_dir_or_file(fname); | ||
179 | if (!path) | ||
180 | return; | ||
181 | if (asprintf(&full_path, "%s/%s", path, fname) == -1) | ||
182 | errExit("asprintf"); | ||
183 | } | ||
184 | |||
185 | // add to private-lib list | ||
186 | if (cfg.bin_private_lib == NULL) { | ||
187 | if (asprintf(&cfg.bin_private_lib, "%s,%s",fname, full_path) == -1) | ||
188 | errExit("asprinf"); | ||
189 | } | ||
190 | else { | ||
191 | char *tmp; | ||
192 | if (asprintf(&tmp, "%s,%s,%s", cfg.bin_private_lib, fname, full_path) == -1) | ||
193 | errExit("asprinf"); | ||
194 | free(cfg.bin_private_lib); | ||
195 | cfg.bin_private_lib = tmp; | ||
196 | } | ||
197 | |||
198 | // if full_path is symlink, and the link is in our path, copy both the file and the symlink | ||
199 | if (is_link(full_path)) { | ||
200 | char *actual_path = realpath(full_path, NULL); | ||
201 | if (actual_path) { | ||
202 | if (valid_full_path_file(actual_path)) { | ||
203 | // solving problems such as /bin/sh -> /bin/dash | ||
204 | // copy the real file pointed by symlink | ||
205 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, actual_path, RUN_BIN_DIR); | ||
206 | prog_cnt++; | ||
207 | char *f = strrchr(actual_path, '/'); | ||
208 | if (f && *(++f) !='\0') | ||
209 | report_duplication(f); | ||
210 | } | ||
211 | free(actual_path); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | // copy a file or a symlink | ||
216 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, full_path, RUN_BIN_DIR); | ||
217 | prog_cnt++; | ||
218 | free(full_path); | ||
219 | report_duplication(fname); | ||
220 | } | ||
221 | |||
222 | static void globbing(char *fname) { | ||
223 | assert(fname); | ||
224 | |||
225 | // go directly to duplicate() if no globbing char is present - see man 7 glob | ||
226 | if (strrchr(fname, '*') == NULL && | ||
227 | strrchr(fname, '[') == NULL && | ||
228 | strrchr(fname, '?') == NULL) | ||
229 | return duplicate(fname); | ||
230 | |||
231 | // loop through paths[] | ||
232 | int i = 0; | ||
233 | while (paths[i]) { | ||
234 | // private-bin-no-local can be disabled in /etc/firejail/firejail.config | ||
235 | if (checkcfg(CFG_PRIVATE_BIN_NO_LOCAL) && strstr(paths[i], "local/")) { | ||
236 | i++; | ||
237 | continue; | ||
238 | } | ||
239 | |||
240 | // check file | ||
241 | char *pattern; | ||
242 | if (asprintf(&pattern, "%s/%s", paths[i], fname) == -1) | ||
243 | errExit("asprintf"); | ||
244 | |||
245 | // globbing | ||
246 | glob_t globbuf; | ||
247 | int globerr = glob(pattern, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf); | ||
248 | if (globerr) { | ||
249 | fprintf(stderr, "Error: failed to glob private-bin pattern %s\n", pattern); | ||
250 | exit(1); | ||
251 | } | ||
252 | |||
253 | size_t j; | ||
254 | for (j = 0; j < globbuf.gl_pathc; j++) { | ||
255 | assert(globbuf.gl_pathv[j]); | ||
256 | // testing for GLOB_NOCHECK - no pattern matched returns the original pattern | ||
257 | if (strcmp(globbuf.gl_pathv[j], pattern) == 0) | ||
258 | continue; | ||
259 | |||
260 | duplicate(globbuf.gl_pathv[j]); | ||
261 | } | ||
262 | |||
263 | globfree(&globbuf); | ||
264 | free(pattern); | ||
265 | i++; | ||
266 | } | ||
267 | } | ||
268 | |||
269 | void fs_private_bin_list(void) { | ||
270 | char *private_list = cfg.bin_private_keep; | ||
271 | assert(private_list); | ||
272 | |||
273 | // start timetrace | ||
274 | timetrace_start(); | ||
275 | |||
276 | // create /run/firejail/mnt/bin directory | ||
277 | mkdir_attr(RUN_BIN_DIR, 0755, 0, 0); | ||
278 | |||
279 | if (arg_debug) | ||
280 | printf("Copying files in the new bin directory\n"); | ||
281 | |||
282 | // copy the list of files in the new home directory | ||
283 | char *dlist = strdup(private_list); | ||
284 | if (!dlist) | ||
285 | errExit("strdup"); | ||
286 | |||
287 | char *ptr = strtok(dlist, ","); | ||
288 | globbing(ptr); | ||
289 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
290 | globbing(ptr); | ||
291 | free(dlist); | ||
292 | fs_logger_print(); | ||
293 | |||
294 | // mount-bind | ||
295 | int i = 0; | ||
296 | while (paths[i]) { | ||
297 | struct stat s; | ||
298 | if (stat(paths[i], &s) == 0) { | ||
299 | if (arg_debug) | ||
300 | printf("Mount-bind %s on top of %s\n", RUN_BIN_DIR, paths[i]); | ||
301 | if (mount(RUN_BIN_DIR, paths[i], NULL, MS_BIND|MS_REC, NULL) < 0) | ||
302 | errExit("mount bind"); | ||
303 | fs_logger2("tmpfs", paths[i]); | ||
304 | fs_logger2("mount", paths[i]); | ||
305 | } | ||
306 | i++; | ||
307 | } | ||
308 | fmessage("%d %s installed in %0.2f ms\n", prog_cnt, (prog_cnt == 1)? "program": "programs", timetrace_end()); | ||
309 | } | ||
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 3afa3bf0c..f8e7e6e74 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -364,6 +364,7 @@ void fs_check_private_dir(void) { | |||
364 | } | 364 | } |
365 | } | 365 | } |
366 | 366 | ||
367 | #ifndef LTS | ||
367 | //*********************************************************************************** | 368 | //*********************************************************************************** |
368 | // --private-home | 369 | // --private-home |
369 | //*********************************************************************************** | 370 | //*********************************************************************************** |
@@ -531,3 +532,4 @@ void fs_private_home_list(void) { | |||
531 | fprintf(stderr, "Home directory installed in %0.2f ms\n", timetrace_end()); | 532 | fprintf(stderr, "Home directory installed in %0.2f ms\n", timetrace_end()); |
532 | 533 | ||
533 | } | 534 | } |
535 | #endif //LTS \ No newline at end of file | ||
diff --git a/src/firejail/fs_lib.c b/src/firejail/fs_lib.c deleted file mode 100644 index 77c9a0cf5..000000000 --- a/src/firejail/fs_lib.c +++ /dev/null | |||
@@ -1,378 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 "../include/ldd_utils.h" | ||
22 | #include <sys/mount.h> | ||
23 | #include <sys/stat.h> | ||
24 | #include <sys/types.h> | ||
25 | #include <unistd.h> | ||
26 | #include <dirent.h> | ||
27 | #include <glob.h> | ||
28 | #define MAXBUF 4096 | ||
29 | |||
30 | extern void fslib_install_stdc(void); | ||
31 | extern void fslib_install_system(void); | ||
32 | |||
33 | static int lib_cnt = 0; | ||
34 | static int dir_cnt = 0; | ||
35 | |||
36 | static void report_duplication(const char *full_path) { | ||
37 | char *fname = strrchr(full_path, '/'); | ||
38 | if (fname && *(++fname) != '\0') { | ||
39 | // report the file on all bin paths | ||
40 | int i = 0; | ||
41 | while (default_lib_paths[i]) { | ||
42 | char *p; | ||
43 | if (asprintf(&p, "%s/%s", default_lib_paths[i], fname) == -1) | ||
44 | errExit("asprintf"); | ||
45 | fs_logger2("clone", p); | ||
46 | free(p); | ||
47 | i++; | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | static char *build_dest_dir(const char *full_path) { | ||
53 | assert(full_path); | ||
54 | if (strstr(full_path, "/x86_64-linux-gnu/")) | ||
55 | return RUN_LIB_DIR "/x86_64-linux-gnu"; | ||
56 | return RUN_LIB_DIR; | ||
57 | } | ||
58 | |||
59 | // copy fname in private_run_dir | ||
60 | void fslib_duplicate(const char *full_path) { | ||
61 | assert(full_path); | ||
62 | |||
63 | struct stat s; | ||
64 | if (stat(full_path, &s) != 0 || s.st_uid != 0 || access(full_path, R_OK)) | ||
65 | return; | ||
66 | |||
67 | char *dest_dir = build_dest_dir(full_path); | ||
68 | |||
69 | // don't copy it if the file is already there | ||
70 | char *ptr = strrchr(full_path, '/'); | ||
71 | if (!ptr) | ||
72 | return; | ||
73 | ptr++; | ||
74 | if (*ptr == '\0') | ||
75 | return; | ||
76 | |||
77 | char *name; | ||
78 | if (asprintf(&name, "%s/%s", dest_dir, ptr) == -1) | ||
79 | errExit("asprintf"); | ||
80 | if (stat(name, &s) == 0) { | ||
81 | free(name); | ||
82 | return; | ||
83 | } | ||
84 | free(name); | ||
85 | |||
86 | if (arg_debug || arg_debug_private_lib) | ||
87 | printf(" copying %s to private %s\n", full_path, dest_dir); | ||
88 | |||
89 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", full_path, dest_dir); | ||
90 | report_duplication(full_path); | ||
91 | lib_cnt++; | ||
92 | } | ||
93 | |||
94 | |||
95 | // requires full path for lib | ||
96 | // it could be a library or an executable | ||
97 | // lib is not copied, only libraries used by it | ||
98 | void fslib_copy_libs(const char *full_path) { | ||
99 | assert(full_path); | ||
100 | if (arg_debug || arg_debug_private_lib) | ||
101 | printf(" fslib_copy_libs %s\n", full_path); | ||
102 | |||
103 | // if library/executable does not exist or the user does not have read access to it | ||
104 | // print a warning and exit the function. | ||
105 | if (access(full_path, R_OK)) { | ||
106 | if (arg_debug || arg_debug_private_lib) | ||
107 | printf("cannot find %s for private-lib, skipping...\n", full_path); | ||
108 | return; | ||
109 | } | ||
110 | |||
111 | // create an empty RUN_LIB_FILE and allow the user to write to it | ||
112 | unlink(RUN_LIB_FILE); // in case is there | ||
113 | create_empty_file_as_root(RUN_LIB_FILE, 0644); | ||
114 | if (chown(RUN_LIB_FILE, getuid(), getgid())) | ||
115 | errExit("chown"); | ||
116 | |||
117 | // run fldd to extact the list of files | ||
118 | if (arg_debug || arg_debug_private_lib) | ||
119 | printf(" running fldd %s\n", full_path); | ||
120 | sbox_run(SBOX_USER | SBOX_SECCOMP | SBOX_CAPS_NONE, 3, PATH_FLDD, full_path, RUN_LIB_FILE); | ||
121 | |||
122 | // open the list of libraries and install them on by one | ||
123 | FILE *fp = fopen(RUN_LIB_FILE, "r"); | ||
124 | if (!fp) | ||
125 | errExit("fopen"); | ||
126 | |||
127 | char buf[MAXBUF]; | ||
128 | while (fgets(buf, MAXBUF, fp)) { | ||
129 | // remove \n | ||
130 | char *ptr = strchr(buf, '\n'); | ||
131 | if (ptr) | ||
132 | *ptr = '\0'; | ||
133 | fslib_duplicate(buf); | ||
134 | } | ||
135 | fclose(fp); | ||
136 | } | ||
137 | |||
138 | |||
139 | void fslib_copy_dir(const char *full_path) { | ||
140 | assert(full_path); | ||
141 | if (arg_debug || arg_debug_private_lib) | ||
142 | printf(" fslib_copy_dir %s\n", full_path); | ||
143 | |||
144 | // do nothing if the directory does not exist or is not owned by root | ||
145 | struct stat s; | ||
146 | if (stat(full_path, &s) != 0 || s.st_uid != 0 || !S_ISDIR(s.st_mode) || access(full_path, R_OK)) | ||
147 | return; | ||
148 | |||
149 | char *dir_name = strrchr(full_path, '/'); | ||
150 | assert(dir_name); | ||
151 | dir_name++; | ||
152 | assert(*dir_name != '\0'); | ||
153 | |||
154 | // do nothing if the directory is already there | ||
155 | char *dest; | ||
156 | if (asprintf(&dest, "%s/%s", build_dest_dir(full_path), dir_name) == -1) | ||
157 | errExit("asprintf"); | ||
158 | if (stat(dest, &s) == 0) { | ||
159 | free(dest); | ||
160 | return; | ||
161 | } | ||
162 | |||
163 | // create new directory and mount the original on top of it | ||
164 | mkdir_attr(dest, 0755, 0, 0); | ||
165 | |||
166 | if (mount(full_path, dest, NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
167 | mount(NULL, dest, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
168 | errExit("mount bind"); | ||
169 | fs_logger2("clone", full_path); | ||
170 | fs_logger2("mount", full_path); | ||
171 | dir_cnt++; | ||
172 | free(dest); | ||
173 | } | ||
174 | |||
175 | // fname should be a vallid full path at this point | ||
176 | static void load_library(const char *fname) { | ||
177 | assert(fname); | ||
178 | assert(*fname == '/'); | ||
179 | |||
180 | // existing file owned by root, read access | ||
181 | struct stat s; | ||
182 | if (stat(fname, &s) == 0 && s.st_uid == 0 && !access(fname, R_OK)) { | ||
183 | // load directories, regular 64 bit libraries, and 64 bit executables | ||
184 | if (is_dir(fname) || is_lib_64(fname)) { | ||
185 | if (is_dir(fname)) | ||
186 | fslib_copy_dir(fname); | ||
187 | else { | ||
188 | if (strstr(fname, ".so") || | ||
189 | access(fname, X_OK) != 0) // don't duplicate executables, just install the libraries | ||
190 | fslib_duplicate(fname); | ||
191 | |||
192 | fslib_copy_libs(fname); | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | |||
198 | static void install_list_entry(const char *lib) { | ||
199 | // filename check | ||
200 | int len = strlen(lib); | ||
201 | if (strcspn(lib, "\\&!?\"'<>%^(){}[];,") != (size_t)len || | ||
202 | strstr(lib, "..")) { | ||
203 | fprintf(stderr, "Error: \"%s\" is an invalid library\n", lib); | ||
204 | exit(1); | ||
205 | } | ||
206 | |||
207 | // if this is a full path, use it as is | ||
208 | if (*lib == '/') | ||
209 | return load_library(lib); | ||
210 | |||
211 | |||
212 | // find the library | ||
213 | int i; | ||
214 | for (i = 0; default_lib_paths[i]; i++) { | ||
215 | char *fname = NULL; | ||
216 | if (asprintf(&fname, "%s/%s", default_lib_paths[i], lib) == -1) | ||
217 | errExit("asprintf"); | ||
218 | |||
219 | #define DO_GLOBBING | ||
220 | #ifdef DO_GLOBBING | ||
221 | // globbing | ||
222 | glob_t globbuf; | ||
223 | int globerr = glob(fname, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf); | ||
224 | if (globerr) { | ||
225 | fprintf(stderr, "Error: failed to glob private-lib pattern %s\n", fname); | ||
226 | exit(1); | ||
227 | } | ||
228 | size_t j; | ||
229 | for (j = 0; j < globbuf.gl_pathc; j++) { | ||
230 | assert(globbuf.gl_pathv[j]); | ||
231 | //printf("glob %s\n", globbuf.gl_pathv[j]); | ||
232 | // GLOB_NOCHECK - no pattern matched returns the original pattern; try to load it anyway | ||
233 | load_library(globbuf.gl_pathv[j]); | ||
234 | } | ||
235 | |||
236 | globfree(&globbuf); | ||
237 | #else | ||
238 | load_library(fname); | ||
239 | #endif | ||
240 | free(fname); | ||
241 | } | ||
242 | |||
243 | // fwarning("%s library not found, skipping...\n", lib); | ||
244 | return; | ||
245 | } | ||
246 | |||
247 | |||
248 | void fslib_install_list(const char *lib_list) { | ||
249 | assert(lib_list); | ||
250 | if (arg_debug || arg_debug_private_lib) | ||
251 | printf(" fslib_install_list %s\n", lib_list); | ||
252 | |||
253 | char *dlist = strdup(lib_list); | ||
254 | if (!dlist) | ||
255 | errExit("strdup"); | ||
256 | |||
257 | char *ptr = strtok(dlist, ","); | ||
258 | install_list_entry(ptr); | ||
259 | |||
260 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
261 | install_list_entry(ptr); | ||
262 | free(dlist); | ||
263 | fs_logger_print(); | ||
264 | } | ||
265 | |||
266 | |||
267 | |||
268 | static void mount_directories(void) { | ||
269 | if (arg_debug || arg_debug_private_lib) | ||
270 | printf("Mount-bind %s on top of /lib /lib64 /usr/lib\n", RUN_LIB_DIR); | ||
271 | |||
272 | if (is_dir("/lib")) { | ||
273 | if (mount(RUN_LIB_DIR, "/lib", NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
274 | mount(NULL, "/lib", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
275 | errExit("mount bind"); | ||
276 | fs_logger2("tmpfs", "/lib"); | ||
277 | fs_logger("mount /lib"); | ||
278 | } | ||
279 | |||
280 | if (is_dir("/lib64")) { | ||
281 | if (mount(RUN_LIB_DIR, "/lib64", NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
282 | mount(NULL, "/lib64", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
283 | errExit("mount bind"); | ||
284 | fs_logger2("tmpfs", "/lib64"); | ||
285 | fs_logger("mount /lib64"); | ||
286 | } | ||
287 | |||
288 | if (is_dir("/usr/lib")) { | ||
289 | if (mount(RUN_LIB_DIR, "/usr/lib", NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
290 | mount(NULL, "/usr/lib", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
291 | errExit("mount bind"); | ||
292 | fs_logger2("tmpfs", "/usr/lib"); | ||
293 | fs_logger("mount /usr/lib"); | ||
294 | } | ||
295 | |||
296 | // for amd64 only - we'll deal with i386 later | ||
297 | if (is_dir("/lib32")) { | ||
298 | if (mount(RUN_RO_DIR, "/lib32", "none", MS_BIND, "mode=400,gid=0") < 0) | ||
299 | errExit("disable file"); | ||
300 | fs_logger("blacklist-nolog /lib32"); | ||
301 | } | ||
302 | if (is_dir("/libx32")) { | ||
303 | if (mount(RUN_RO_DIR, "/libx32", "none", MS_BIND, "mode=400,gid=0") < 0) | ||
304 | errExit("disable file"); | ||
305 | fs_logger("blacklist-nolog /libx32"); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | void fs_private_lib(void) { | ||
310 | #ifndef __x86_64__ | ||
311 | fwarning("private-lib feature is currently available only on amd64 platforms\n"); | ||
312 | return; | ||
313 | #endif | ||
314 | char *private_list = cfg.lib_private_keep; | ||
315 | if (arg_debug || arg_debug_private_lib) | ||
316 | printf("Starting private-lib processing: program %s, shell %s\n", | ||
317 | (cfg.original_program_index > 0)? cfg.original_argv[cfg.original_program_index]: "none", | ||
318 | (arg_shell_none)? "none": cfg.shell); | ||
319 | |||
320 | // create /run/firejail/mnt/lib directory | ||
321 | mkdir_attr(RUN_LIB_DIR, 0755, 0, 0); | ||
322 | |||
323 | // install standard C libraries | ||
324 | if (arg_debug || arg_debug_private_lib) | ||
325 | printf("Installing standard C library\n"); | ||
326 | fslib_install_stdc(); | ||
327 | |||
328 | // start timetrace | ||
329 | timetrace_start(); | ||
330 | |||
331 | // copy the libs in the new lib directory for the main exe | ||
332 | if (cfg.original_program_index > 0) { | ||
333 | if (arg_debug || arg_debug_private_lib) | ||
334 | printf("Installing sandboxed program libraries\n"); | ||
335 | fslib_install_list(cfg.original_argv[cfg.original_program_index]); | ||
336 | } | ||
337 | |||
338 | // for the shell | ||
339 | if (!arg_shell_none) { | ||
340 | if (arg_debug || arg_debug_private_lib) | ||
341 | printf("Installing shell libraries\n"); | ||
342 | |||
343 | fslib_install_list(cfg.shell); | ||
344 | // a shell is useless without some basic commands | ||
345 | fslib_install_list("/bin/ls,/bin/cat,/bin/mv,/bin/rm"); | ||
346 | |||
347 | } | ||
348 | |||
349 | // for the listed libs and directories | ||
350 | if (private_list && *private_list != '\0') { | ||
351 | if (arg_debug || arg_debug_private_lib) | ||
352 | printf("Processing private-lib files\n"); | ||
353 | fslib_install_list(private_list); | ||
354 | } | ||
355 | |||
356 | // for private-bin files | ||
357 | if (arg_private_bin && cfg.bin_private_lib && *cfg.bin_private_lib != '\0') { | ||
358 | if (arg_debug || arg_debug_private_lib) | ||
359 | printf("Processing private-bin files\n"); | ||
360 | fslib_install_list(cfg.bin_private_lib); | ||
361 | } | ||
362 | fmessage("Program libraries installed in %0.2f ms\n", timetrace_end()); | ||
363 | |||
364 | // install the reset of the system libraries | ||
365 | if (arg_debug || arg_debug_private_lib) | ||
366 | printf("Installing system libraries\n"); | ||
367 | fslib_install_system(); | ||
368 | |||
369 | // bring in firejail directory for --trace and seccomp post exec | ||
370 | // bring in firejail executable libraries in case we are redirected here by a firejail symlink from /usr/local/bin/firejail | ||
371 | fslib_install_list("/usr/bin/firejail,firejail"); // todo: use the installed path for the executable | ||
372 | |||
373 | fmessage("Installed %d %s and %d %s\n", lib_cnt, (lib_cnt == 1)? "library": "libraries", | ||
374 | dir_cnt, (dir_cnt == 1)? "directory": "directories"); | ||
375 | |||
376 | // mount lib filesystem | ||
377 | mount_directories(); | ||
378 | } | ||
diff --git a/src/firejail/fs_lib2.c b/src/firejail/fs_lib2.c deleted file mode 100644 index ea5edfabe..000000000 --- a/src/firejail/fs_lib2.c +++ /dev/null | |||
@@ -1,314 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 <dirent.h> | ||
22 | #include <sys/stat.h> | ||
23 | |||
24 | extern void fslib_duplicate(const char *full_path); | ||
25 | extern void fslib_copy_libs(const char *full_path); | ||
26 | extern void fslib_copy_dir(const char *full_path); | ||
27 | |||
28 | //*************************************************************** | ||
29 | // Standard C library | ||
30 | //*************************************************************** | ||
31 | // standard libc libraries based on Debian's libc6 package | ||
32 | // selinux seems to be linked in most command line utilities | ||
33 | // locale (/usr/lib/locale) - without it, the program will default to "C" locale | ||
34 | typedef struct liblist_t { | ||
35 | const char *name; | ||
36 | int len; | ||
37 | } LibList; | ||
38 | |||
39 | static LibList libc_list[] = { | ||
40 | { "libselinux.so.", 0 }, | ||
41 | { "ld-linux-x86-64.so.", 0 }, | ||
42 | { "libanl.so.", 0 }, | ||
43 | { "libc.so.", 0 }, | ||
44 | { "libcidn.so.", 0 }, | ||
45 | { "libcrypt.so.", 0 }, | ||
46 | { "libdl.so.", 0 }, | ||
47 | { "libm.so.", 0 }, | ||
48 | { "libmemusage.so", 0 }, | ||
49 | { "libmvec.so.", 0 }, | ||
50 | { "libnsl.so.", 0 }, | ||
51 | { "libnss_compat.so.", 0 }, | ||
52 | { "libnss_dns.so.", 0 }, | ||
53 | { "libnss_files.so.", 0 }, | ||
54 | { "libnss_hesiod.so.", 0 }, | ||
55 | { "libnss_nisplus.so.", 0 }, | ||
56 | { "libnss_nis.so.", 0 }, | ||
57 | { "libpthread.so.", 0 }, | ||
58 | { "libresolv.so.", 0 }, | ||
59 | { "librt.so.", 0 }, | ||
60 | { "libthread_db.so.", 0 }, | ||
61 | { "libutil.so.", 0 }, | ||
62 | { NULL, 0} | ||
63 | }; | ||
64 | |||
65 | static int find_libc_list(const char *name) { | ||
66 | assert(name); | ||
67 | |||
68 | int i = 0; | ||
69 | while (libc_list[i].name) { | ||
70 | if (libc_list[i].len == 0) | ||
71 | libc_list[i].len = strlen(libc_list[i].name); | ||
72 | if (strncmp(name, libc_list[i].name, libc_list[i].len) == 0) | ||
73 | return 1; | ||
74 | i++; | ||
75 | } | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | // compare the files in dirname against liblist above | ||
80 | static void stdc(const char *dirname) { | ||
81 | assert(dirname); | ||
82 | |||
83 | DIR *dir = opendir(dirname); | ||
84 | if (dir) { | ||
85 | struct dirent *entry; | ||
86 | while ((entry = readdir(dir)) != NULL) { | ||
87 | if (strcmp(entry->d_name, ".") == 0) | ||
88 | continue; | ||
89 | if (strcmp(entry->d_name, "..") == 0) | ||
90 | continue; | ||
91 | |||
92 | if (find_libc_list(entry->d_name)) { | ||
93 | char *fname; | ||
94 | if (asprintf(&fname, "%s/%s", dirname, entry->d_name) == -1) | ||
95 | errExit("asprintf"); | ||
96 | |||
97 | fslib_duplicate(fname); | ||
98 | } | ||
99 | } | ||
100 | closedir(dir); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | void fslib_install_stdc(void) { | ||
105 | // install standard C libraries | ||
106 | struct stat s; | ||
107 | char *stdclib = "/lib64"; // CentOS, Fedora, Arch | ||
108 | |||
109 | if (stat("/lib/x86_64-linux-gnu", &s) == 0) { // Debian & friends | ||
110 | mkdir_attr(RUN_LIB_DIR "/x86_64-linux-gnu", 0755, 0, 0); | ||
111 | stdclib = "/lib/x86_64-linux-gnu"; | ||
112 | } | ||
113 | |||
114 | timetrace_start(); | ||
115 | stdc(stdclib); | ||
116 | |||
117 | // install locale | ||
118 | if (stat("/usr/lib/locale", &s) == 0) | ||
119 | fslib_copy_dir("/usr/lib/locale"); | ||
120 | |||
121 | fmessage("Standard C library installed in %0.2f ms\n", timetrace_end()); | ||
122 | } | ||
123 | |||
124 | |||
125 | //*************************************************************** | ||
126 | // various system libraries | ||
127 | //*************************************************************** | ||
128 | |||
129 | // look for library in the new filesystem, and install one or two more directories, dir1 and dir2 | ||
130 | typedef struct syslib_t { | ||
131 | const char *library; // look in the system for this library | ||
132 | int len; // length of library string, 0 by default | ||
133 | int found; // library found, 0 by default | ||
134 | const char *dir1; // directory to install | ||
135 | const char *dir2; // directory to install | ||
136 | const char *message; // message to print on the screen | ||
137 | } SysLib; | ||
138 | |||
139 | SysLib syslibs[] = { | ||
140 | #if 0 | ||
141 | { | ||
142 | "", // library | ||
143 | 0, 0, // len and found flag | ||
144 | "", // dir1 | ||
145 | "", // dir2 | ||
146 | "" // message | ||
147 | }, | ||
148 | #endif | ||
149 | { // pixmaps - libraries used by GTK to display application menu icons | ||
150 | "libgdk_pixbuf-2.0", // library | ||
151 | 0, 0, // len and found flag | ||
152 | "gdk-pixbuf-2.0", // dir1 | ||
153 | "", // dir2 | ||
154 | "GdkPixbuf" // message | ||
155 | }, | ||
156 | { // GTK2 | ||
157 | "libgtk-x11-2.0", // library | ||
158 | 0, 0, // len and found flag | ||
159 | "gtk-2.0", // dir1 | ||
160 | "libgtk2.0-0", // dir2 | ||
161 | "GTK2" // message | ||
162 | }, | ||
163 | { // GTK3 | ||
164 | "libgtk-3", // library | ||
165 | 0, 0, // len and found flag | ||
166 | "gtk-3.0", // dir1 | ||
167 | "libgtk-3-0", // dir2 | ||
168 | "GTK3" // message | ||
169 | }, | ||
170 | { // Pango - text internationalization, found on older GTK2-based systems | ||
171 | "libpango", // library | ||
172 | 0, 0, // len and found flag | ||
173 | "pango", // dir1 | ||
174 | "", // dir2 | ||
175 | "Pango" // message | ||
176 | }, | ||
177 | { // Library for handling GObject introspection data on GTK systems | ||
178 | "libgirepository-1.0", // library | ||
179 | 0, 0, // len and found flag | ||
180 | "girepository-1.0", // dir1 | ||
181 | "", // dir2 | ||
182 | "GIRepository" // message | ||
183 | }, | ||
184 | { // GIO | ||
185 | "libgio", // library | ||
186 | 0, 0, // len and found flag | ||
187 | "gio", // dir1 | ||
188 | "", // dir2 | ||
189 | "GIO" // message | ||
190 | }, | ||
191 | { // Enchant speller | ||
192 | "libenchant.so.", // library | ||
193 | 0, 0, // len and found flag | ||
194 | "enchant", // dir1 | ||
195 | "", // dir2 | ||
196 | "Enchant (speller)" // message | ||
197 | }, | ||
198 | { // Qt5 - lots of problems on Arch Linux, Qt5 version 5.9.1 - disabled in all apps profiles | ||
199 | "libQt5", // library | ||
200 | 0, 0, // len and found flag | ||
201 | "qt5", // dir1 | ||
202 | "gdk-pixbuf-2.0", // dir2 | ||
203 | "Qt5, GdkPixbuf" // message | ||
204 | }, | ||
205 | { // Qt4 | ||
206 | "libQtCore", // library | ||
207 | 0, 0, // len and found flag | ||
208 | "qt4", // dir1 | ||
209 | "gdk-pixbuf-2.0", // dir2 | ||
210 | "Qt4" // message | ||
211 | }, | ||
212 | |||
213 | { // NULL terminated list | ||
214 | NULL, // library | ||
215 | 0, 0, // len and found flag | ||
216 | "", // dir1 | ||
217 | "", // dir2 | ||
218 | "" // message | ||
219 | } | ||
220 | }; | ||
221 | |||
222 | void fslib_install_system(void) { | ||
223 | // look for installed libraries | ||
224 | DIR *dir = opendir(RUN_LIB_DIR "/x86_64-linux-gnu"); | ||
225 | if (!dir) | ||
226 | dir = opendir(RUN_LIB_DIR); | ||
227 | |||
228 | if (dir) { | ||
229 | struct dirent *entry; | ||
230 | while ((entry = readdir(dir)) != NULL) { | ||
231 | if (strcmp(entry->d_name, ".") == 0) | ||
232 | continue; | ||
233 | if (strcmp(entry->d_name, "..") == 0) | ||
234 | continue; | ||
235 | |||
236 | SysLib *ptr = &syslibs[0]; | ||
237 | while (ptr->library) { | ||
238 | if (ptr->len == 0) | ||
239 | ptr->len = strlen(ptr->library); | ||
240 | |||
241 | if (strncmp(entry->d_name, ptr->library, ptr->len) == 0) { | ||
242 | ptr->found = 1; | ||
243 | break; | ||
244 | } | ||
245 | |||
246 | ptr++; | ||
247 | } | ||
248 | |||
249 | } | ||
250 | closedir(dir); | ||
251 | } | ||
252 | else | ||
253 | assert(0); | ||
254 | |||
255 | // install required directories | ||
256 | SysLib *ptr = &syslibs[0]; | ||
257 | while (ptr->library) { | ||
258 | if (ptr->found) { | ||
259 | assert(*ptr->message != '\0'); | ||
260 | timetrace_start(); | ||
261 | |||
262 | // bring in all libraries | ||
263 | assert(ptr->dir1); | ||
264 | char *name; | ||
265 | // Debian & friends | ||
266 | if (asprintf(&name, "/usr/lib/x86_64-linux-gnu/%s", ptr->dir1) == -1) | ||
267 | errExit("asprintf"); | ||
268 | if (access(name, R_OK) == 0) { | ||
269 | fslib_copy_libs(name); | ||
270 | fslib_copy_dir(name); | ||
271 | } | ||
272 | else { | ||
273 | free(name); | ||
274 | // CentOS, Fedora, Arch | ||
275 | if (asprintf(&name, "/usr/lib64/%s", ptr->dir1) == -1) | ||
276 | errExit("asprintf"); | ||
277 | if (access(name, R_OK) == 0) { | ||
278 | fslib_copy_libs(name); | ||
279 | fslib_copy_dir(name); | ||
280 | } | ||
281 | } | ||
282 | free(name); | ||
283 | |||
284 | if (*ptr->dir2 != '\0') { | ||
285 | // Debian & friends | ||
286 | if (asprintf(&name, "/usr/lib/x86_64-linux-gnu/%s", ptr->dir2) == -1) | ||
287 | errExit("asprintf"); | ||
288 | if (access(name, R_OK) == 0) { | ||
289 | fslib_copy_libs(name); | ||
290 | fslib_copy_dir(name); | ||
291 | } | ||
292 | else { | ||
293 | free(name); | ||
294 | // CentOS, Fedora, Arch | ||
295 | if (asprintf(&name, "/usr/lib64/%s", ptr->dir2) == -1) | ||
296 | errExit("asprintf"); | ||
297 | if (access(name, R_OK) == 0) { | ||
298 | fslib_copy_libs(name); | ||
299 | fslib_copy_dir(name); | ||
300 | } | ||
301 | } | ||
302 | free(name); | ||
303 | } | ||
304 | |||
305 | fmessage("%s installed in %0.2f ms\n", ptr->message, timetrace_end()); | ||
306 | } | ||
307 | ptr++; | ||
308 | } | ||
309 | } | ||
310 | |||
311 | |||
312 | |||
313 | |||
314 | |||
diff --git a/src/firejail/main.c b/src/firejail/main.c index 3e092a3cc..ba5e8cdfd 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -339,6 +339,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
339 | exit(0); | 339 | exit(0); |
340 | } | 340 | } |
341 | #endif | 341 | #endif |
342 | #ifndef LTS | ||
342 | #ifdef HAVE_X11 | 343 | #ifdef HAVE_X11 |
343 | else if (strcmp(argv[i], "--x11") == 0) { | 344 | else if (strcmp(argv[i], "--x11") == 0) { |
344 | if (checkcfg(CFG_X11)) { | 345 | if (checkcfg(CFG_X11)) { |
@@ -373,6 +374,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
373 | exit_err_feature("x11"); | 374 | exit_err_feature("x11"); |
374 | } | 375 | } |
375 | #endif | 376 | #endif |
377 | #endif // LTS | ||
376 | #ifdef HAVE_NETWORK | 378 | #ifdef HAVE_NETWORK |
377 | else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { | 379 | else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { |
378 | if (checkcfg(CFG_NETWORK)) { | 380 | if (checkcfg(CFG_NETWORK)) { |
@@ -825,6 +827,7 @@ static int check_arg(int argc, char **argv, const char *argument, int strict) { | |||
825 | return found; | 827 | return found; |
826 | } | 828 | } |
827 | 829 | ||
830 | #ifndef LTS | ||
828 | static void run_builder(int argc, char **argv) { | 831 | static void run_builder(int argc, char **argv) { |
829 | EUID_ASSERT(); | 832 | EUID_ASSERT(); |
830 | (void) argc; | 833 | (void) argc; |
@@ -844,7 +847,7 @@ static void run_builder(int argc, char **argv) { | |||
844 | perror("execvp"); | 847 | perror("execvp"); |
845 | exit(1); | 848 | exit(1); |
846 | } | 849 | } |
847 | 850 | #endif // LTS | |
848 | 851 | ||
849 | //******************************************* | 852 | //******************************************* |
850 | // Main program | 853 | // Main program |
@@ -920,10 +923,11 @@ int main(int argc, char **argv) { | |||
920 | profile_add(cmd); | 923 | profile_add(cmd); |
921 | } | 924 | } |
922 | 925 | ||
926 | #ifndef LTS | ||
923 | // profile builder | 927 | // profile builder |
924 | if (check_arg(argc, argv, "--build", 0)) // supports both --build and --build=filename | 928 | if (check_arg(argc, argv, "--build", 0)) // supports both --build and --build=filename |
925 | run_builder(argc, argv); // this function will not return | 929 | run_builder(argc, argv); // this function will not return |
926 | 930 | #endif // LTS | |
927 | // check argv[0] symlink wrapper if this is not a login shell | 931 | // check argv[0] symlink wrapper if this is not a login shell |
928 | if (*argv[0] != '-') | 932 | if (*argv[0] != '-') |
929 | run_symlink(argc, argv, 0); // if symlink detected, this function will not return | 933 | run_symlink(argc, argv, 0); // if symlink detected, this function will not return |
@@ -1354,6 +1358,7 @@ int main(int argc, char **argv) { | |||
1354 | } | 1358 | } |
1355 | else if (strcmp(argv[i], "--disable-mnt") == 0) | 1359 | else if (strcmp(argv[i], "--disable-mnt") == 0) |
1356 | arg_disable_mnt = 1; | 1360 | arg_disable_mnt = 1; |
1361 | #ifndef LTS | ||
1357 | #ifdef HAVE_OVERLAYFS | 1362 | #ifdef HAVE_OVERLAYFS |
1358 | else if (strcmp(argv[i], "--overlay") == 0) { | 1363 | else if (strcmp(argv[i], "--overlay") == 0) { |
1359 | if (checkcfg(CFG_OVERLAYFS)) { | 1364 | if (checkcfg(CFG_OVERLAYFS)) { |
@@ -1441,6 +1446,7 @@ int main(int argc, char **argv) { | |||
1441 | exit_err_feature("overlayfs"); | 1446 | exit_err_feature("overlayfs"); |
1442 | } | 1447 | } |
1443 | #endif | 1448 | #endif |
1449 | #endif //LTS | ||
1444 | else if (strncmp(argv[i], "--profile=", 10) == 0) { | 1450 | else if (strncmp(argv[i], "--profile=", 10) == 0) { |
1445 | // multiple profile files are allowed! | 1451 | // multiple profile files are allowed! |
1446 | 1452 | ||
@@ -1489,6 +1495,7 @@ int main(int argc, char **argv) { | |||
1489 | else | 1495 | else |
1490 | cfg.profile_ignore[j] = argv[i] + 9; | 1496 | cfg.profile_ignore[j] = argv[i] + 9; |
1491 | } | 1497 | } |
1498 | #ifndef LTS | ||
1492 | #ifdef HAVE_CHROOT | 1499 | #ifdef HAVE_CHROOT |
1493 | else if (strncmp(argv[i], "--chroot=", 9) == 0) { | 1500 | else if (strncmp(argv[i], "--chroot=", 9) == 0) { |
1494 | if (checkcfg(CFG_CHROOT)) { | 1501 | if (checkcfg(CFG_CHROOT)) { |
@@ -1537,6 +1544,7 @@ int main(int argc, char **argv) { | |||
1537 | exit_err_feature("chroot"); | 1544 | exit_err_feature("chroot"); |
1538 | } | 1545 | } |
1539 | #endif | 1546 | #endif |
1547 | #endif // LTS | ||
1540 | else if (strcmp(argv[i], "--writable-etc") == 0) { | 1548 | else if (strcmp(argv[i], "--writable-etc") == 0) { |
1541 | if (cfg.etc_private_keep) { | 1549 | if (cfg.etc_private_keep) { |
1542 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); | 1550 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); |
@@ -1583,6 +1591,7 @@ int main(int argc, char **argv) { | |||
1583 | } | 1591 | } |
1584 | arg_private = 1; | 1592 | arg_private = 1; |
1585 | } | 1593 | } |
1594 | #ifndef LTS | ||
1586 | #ifdef HAVE_PRIVATE_HOME | 1595 | #ifdef HAVE_PRIVATE_HOME |
1587 | else if (strncmp(argv[i], "--private-home=", 15) == 0) { | 1596 | else if (strncmp(argv[i], "--private-home=", 15) == 0) { |
1588 | if (checkcfg(CFG_PRIVATE_HOME)) { | 1597 | if (checkcfg(CFG_PRIVATE_HOME)) { |
@@ -1607,6 +1616,7 @@ int main(int argc, char **argv) { | |||
1607 | exit_err_feature("private-home"); | 1616 | exit_err_feature("private-home"); |
1608 | } | 1617 | } |
1609 | #endif | 1618 | #endif |
1619 | #endif //LTS | ||
1610 | else if (strcmp(argv[i], "--private-dev") == 0) { | 1620 | else if (strcmp(argv[i], "--private-dev") == 0) { |
1611 | arg_private_dev = 1; | 1621 | arg_private_dev = 1; |
1612 | } | 1622 | } |
@@ -1657,6 +1667,7 @@ int main(int argc, char **argv) { | |||
1657 | cfg.srv_private_keep = argv[i] + 14; | 1667 | cfg.srv_private_keep = argv[i] + 14; |
1658 | arg_private_srv = 1; | 1668 | arg_private_srv = 1; |
1659 | } | 1669 | } |
1670 | #ifndef LTS | ||
1660 | else if (strncmp(argv[i], "--private-bin=", 14) == 0) { | 1671 | else if (strncmp(argv[i], "--private-bin=", 14) == 0) { |
1661 | // extract private bin list | 1672 | // extract private bin list |
1662 | if (*(argv[i] + 14) == '\0') { | 1673 | if (*(argv[i] + 14) == '\0') { |
@@ -1685,6 +1696,7 @@ int main(int argc, char **argv) { | |||
1685 | else | 1696 | else |
1686 | exit_err_feature("private-lib"); | 1697 | exit_err_feature("private-lib"); |
1687 | } | 1698 | } |
1699 | #endif // LTS | ||
1688 | else if (strcmp(argv[i], "--private-tmp") == 0) { | 1700 | else if (strcmp(argv[i], "--private-tmp") == 0) { |
1689 | arg_private_tmp = 1; | 1701 | arg_private_tmp = 1; |
1690 | } | 1702 | } |
@@ -2100,6 +2112,7 @@ int main(int argc, char **argv) { | |||
2100 | //************************************* | 2112 | //************************************* |
2101 | else if (strncmp(argv[i], "--timeout=", 10) == 0) | 2113 | else if (strncmp(argv[i], "--timeout=", 10) == 0) |
2102 | cfg.timeout = extract_timeout(argv[i] + 10); | 2114 | cfg.timeout = extract_timeout(argv[i] + 10); |
2115 | #ifndef LTS | ||
2103 | else if (strcmp(argv[i], "--audit") == 0) { | 2116 | else if (strcmp(argv[i], "--audit") == 0) { |
2104 | arg_audit_prog = LIBDIR "/firejail/faudit"; | 2117 | arg_audit_prog = LIBDIR "/firejail/faudit"; |
2105 | arg_audit = 1; | 2118 | arg_audit = 1; |
@@ -2120,6 +2133,7 @@ int main(int argc, char **argv) { | |||
2120 | } | 2133 | } |
2121 | arg_audit = 1; | 2134 | arg_audit = 1; |
2122 | } | 2135 | } |
2136 | #endif // LTS | ||
2123 | else if (strcmp(argv[i], "--appimage") == 0) | 2137 | else if (strcmp(argv[i], "--appimage") == 0) |
2124 | arg_appimage = 1; | 2138 | arg_appimage = 1; |
2125 | else if (strcmp(argv[i], "--shell=none") == 0) { | 2139 | else if (strcmp(argv[i], "--shell=none") == 0) { |
@@ -2364,10 +2378,11 @@ int main(int argc, char **argv) { | |||
2364 | } | 2378 | } |
2365 | EUID_ASSERT(); | 2379 | EUID_ASSERT(); |
2366 | 2380 | ||
2381 | #ifndef LTS | ||
2367 | // block X11 sockets | 2382 | // block X11 sockets |
2368 | if (arg_x11_block) | 2383 | if (arg_x11_block) |
2369 | x11_block(); | 2384 | x11_block(); |
2370 | 2385 | #endif //LTS | |
2371 | // check network configuration options - it will exit if anything went wrong | 2386 | // check network configuration options - it will exit if anything went wrong |
2372 | net_check_cfg(); | 2387 | net_check_cfg(); |
2373 | 2388 | ||
@@ -2422,9 +2437,11 @@ int main(int argc, char **argv) { | |||
2422 | } | 2437 | } |
2423 | if (cfg.name) | 2438 | if (cfg.name) |
2424 | set_name_run_file(sandbox_pid); | 2439 | set_name_run_file(sandbox_pid); |
2440 | #ifndef LTS | ||
2425 | int display = x11_display(); | 2441 | int display = x11_display(); |
2426 | if (display > 0) | 2442 | if (display > 0) |
2427 | set_x11_run_file(sandbox_pid, display); | 2443 | set_x11_run_file(sandbox_pid, display); |
2444 | #endif | ||
2428 | flock(lockfd_directory, LOCK_UN); | 2445 | flock(lockfd_directory, LOCK_UN); |
2429 | close(lockfd_directory); | 2446 | close(lockfd_directory); |
2430 | EUID_USER(); | 2447 | EUID_USER(); |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 4b2fb3abd..c3ef2f2f5 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -198,6 +198,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
198 | arg_private = 1; | 198 | arg_private = 1; |
199 | return 0; | 199 | return 0; |
200 | } | 200 | } |
201 | #ifndef LTS | ||
201 | if (strncmp(ptr, "private-home ", 13) == 0) { | 202 | if (strncmp(ptr, "private-home ", 13) == 0) { |
202 | #ifdef HAVE_PRIVATE_HOME | 203 | #ifdef HAVE_PRIVATE_HOME |
203 | if (checkcfg(CFG_PRIVATE_HOME)) { | 204 | if (checkcfg(CFG_PRIVATE_HOME)) { |
@@ -213,6 +214,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
213 | #endif | 214 | #endif |
214 | return 0; | 215 | return 0; |
215 | } | 216 | } |
217 | #endif //LTS | ||
216 | else if (strcmp(ptr, "allusers") == 0) { | 218 | else if (strcmp(ptr, "allusers") == 0) { |
217 | arg_allusers = 1; | 219 | arg_allusers = 1; |
218 | return 0; | 220 | return 0; |
@@ -790,6 +792,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
790 | return 0; | 792 | return 0; |
791 | } | 793 | } |
792 | 794 | ||
795 | #ifndef LTS | ||
793 | if (strcmp(ptr, "x11 xephyr") == 0) { | 796 | if (strcmp(ptr, "x11 xephyr") == 0) { |
794 | #ifdef HAVE_X11 | 797 | #ifdef HAVE_X11 |
795 | if (checkcfg(CFG_X11)) { | 798 | if (checkcfg(CFG_X11)) { |
@@ -875,7 +878,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
875 | #endif | 878 | #endif |
876 | return 0; | 879 | return 0; |
877 | } | 880 | } |
878 | 881 | #endif //LTS | |
879 | // private /etc list of files and directories | 882 | // private /etc list of files and directories |
880 | if (strncmp(ptr, "private-etc ", 12) == 0) { | 883 | if (strncmp(ptr, "private-etc ", 12) == 0) { |
881 | if (arg_writable_etc) { | 884 | if (arg_writable_etc) { |
@@ -949,7 +952,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
949 | return 0; | 952 | return 0; |
950 | } | 953 | } |
951 | 954 | ||
952 | 955 | #ifndef LTS | |
953 | #ifdef HAVE_OVERLAYFS | 956 | #ifdef HAVE_OVERLAYFS |
954 | if (strncmp(ptr, "overlay-named ", 14) == 0) { | 957 | if (strncmp(ptr, "overlay-named ", 14) == 0) { |
955 | if (checkcfg(CFG_OVERLAYFS)) { | 958 | if (checkcfg(CFG_OVERLAYFS)) { |
@@ -1034,6 +1037,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
1034 | } | 1037 | } |
1035 | } | 1038 | } |
1036 | #endif | 1039 | #endif |
1040 | #endif // LTS | ||
1037 | 1041 | ||
1038 | // filesystem bind | 1042 | // filesystem bind |
1039 | if (strncmp(ptr, "bind ", 5) == 0) { | 1043 | if (strncmp(ptr, "bind ", 5) == 0) { |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 66881c040..06c2dbf5b 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -391,6 +391,7 @@ void start_application(int no_sandbox) { | |||
391 | //**************************************** | 391 | //**************************************** |
392 | // audit | 392 | // audit |
393 | //**************************************** | 393 | //**************************************** |
394 | #ifndef LTS | ||
394 | if (arg_audit) { | 395 | if (arg_audit) { |
395 | assert(arg_audit_prog); | 396 | assert(arg_audit_prog); |
396 | #ifdef HAVE_GCOV | 397 | #ifdef HAVE_GCOV |
@@ -404,7 +405,9 @@ void start_application(int no_sandbox) { | |||
404 | //**************************************** | 405 | //**************************************** |
405 | // start the program without using a shell | 406 | // start the program without using a shell |
406 | //**************************************** | 407 | //**************************************** |
407 | else if (arg_shell_none) { | 408 | else |
409 | #endif // LTS | ||
410 | if (arg_shell_none) { | ||
408 | if (arg_debug) { | 411 | if (arg_debug) { |
409 | int i; | 412 | int i; |
410 | for (i = cfg.original_program_index; i < cfg.original_argc; i++) { | 413 | for (i = cfg.original_program_index; i < cfg.original_argc; i++) { |
@@ -732,6 +735,7 @@ int sandbox(void* sandbox_arg) { | |||
732 | if (arg_appimage) | 735 | if (arg_appimage) |
733 | enforce_filters(); | 736 | enforce_filters(); |
734 | 737 | ||
738 | #ifndef LTS | ||
735 | #ifdef HAVE_CHROOT | 739 | #ifdef HAVE_CHROOT |
736 | if (cfg.chrootdir) { | 740 | if (cfg.chrootdir) { |
737 | fs_chroot(cfg.chrootdir); | 741 | fs_chroot(cfg.chrootdir); |
@@ -761,6 +765,7 @@ int sandbox(void* sandbox_arg) { | |||
761 | } | 765 | } |
762 | else | 766 | else |
763 | #endif | 767 | #endif |
768 | #endif // LTS | ||
764 | fs_basic_fs(); | 769 | fs_basic_fs(); |
765 | 770 | ||
766 | //**************************** | 771 | //**************************** |
@@ -775,6 +780,7 @@ int sandbox(void* sandbox_arg) { | |||
775 | else | 780 | else |
776 | fs_private_homedir(); | 781 | fs_private_homedir(); |
777 | } | 782 | } |
783 | #ifndef LTS | ||
778 | else if (cfg.home_private_keep) { // --private-home= | 784 | else if (cfg.home_private_keep) { // --private-home= |
779 | if (cfg.chrootdir) | 785 | if (cfg.chrootdir) |
780 | fwarning("private-home= feature is disabled in chroot\n"); | 786 | fwarning("private-home= feature is disabled in chroot\n"); |
@@ -784,6 +790,7 @@ int sandbox(void* sandbox_arg) { | |||
784 | fs_private_home_list(); | 790 | fs_private_home_list(); |
785 | } | 791 | } |
786 | else // --private | 792 | else // --private |
793 | #endif //LTS | ||
787 | fs_private(); | 794 | fs_private(); |
788 | } | 795 | } |
789 | 796 | ||
@@ -823,6 +830,7 @@ int sandbox(void* sandbox_arg) { | |||
823 | } | 830 | } |
824 | } | 831 | } |
825 | 832 | ||
833 | #ifndef LTS | ||
826 | // private-bin is disabled for appimages | 834 | // private-bin is disabled for appimages |
827 | if (arg_private_bin && !arg_appimage) { | 835 | if (arg_private_bin && !arg_appimage) { |
828 | if (cfg.chrootdir) | 836 | if (cfg.chrootdir) |
@@ -853,6 +861,7 @@ int sandbox(void* sandbox_arg) { | |||
853 | fs_private_lib(); | 861 | fs_private_lib(); |
854 | } | 862 | } |
855 | } | 863 | } |
864 | #endif // LTS | ||
856 | 865 | ||
857 | if (arg_private_cache) { | 866 | if (arg_private_cache) { |
858 | if (cfg.chrootdir) | 867 | if (cfg.chrootdir) |
@@ -1001,10 +1010,12 @@ int sandbox(void* sandbox_arg) { | |||
1001 | } | 1010 | } |
1002 | } | 1011 | } |
1003 | 1012 | ||
1013 | #ifndef LTS | ||
1004 | // clean /tmp/.X11-unix sockets | 1014 | // clean /tmp/.X11-unix sockets |
1005 | fs_x11(); | 1015 | fs_x11(); |
1006 | if (arg_x11_xorg) | 1016 | if (arg_x11_xorg) |
1007 | x11_xorg(); | 1017 | x11_xorg(); |
1018 | #endif //LTS | ||
1008 | 1019 | ||
1009 | //**************************** | 1020 | //**************************** |
1010 | // set security filters | 1021 | // set security filters |
diff --git a/src/firejail/x11.c b/src/firejail/x11.c deleted file mode 100644 index 9cbe6598e..000000000 --- a/src/firejail/x11.c +++ /dev/null | |||
@@ -1,1311 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 <sys/socket.h> | ||
24 | #include <sys/un.h> | ||
25 | #include <unistd.h> | ||
26 | #include <signal.h> | ||
27 | #include <stdlib.h> | ||
28 | #include <dirent.h> | ||
29 | #include <sys/mount.h> | ||
30 | #include <sys/wait.h> | ||
31 | #include <errno.h> | ||
32 | #include <limits.h> | ||
33 | |||
34 | // on Debian 7 we are missing O_PATH definition | ||
35 | #include <fcntl.h> | ||
36 | #ifndef O_PATH | ||
37 | #define O_PATH 010000000 | ||
38 | #endif | ||
39 | |||
40 | |||
41 | // Parse the DISPLAY environment variable and return a display number. | ||
42 | // Returns -1 if DISPLAY is not set, or is set to anything other than :ddd. | ||
43 | int x11_display(void) { | ||
44 | const char *display_str = getenv("DISPLAY"); | ||
45 | char *endp; | ||
46 | unsigned long display; | ||
47 | |||
48 | if (!display_str) { | ||
49 | if (arg_debug) | ||
50 | fputs("DISPLAY is not set\n", stderr); | ||
51 | return -1; | ||
52 | } | ||
53 | |||
54 | if (display_str[0] != ':' || display_str[1] < '0' || display_str[1] > '9') { | ||
55 | if (arg_debug) | ||
56 | fprintf(stderr, "unsupported DISPLAY form '%s'\n", display_str); | ||
57 | return -1; | ||
58 | } | ||
59 | |||
60 | errno = 0; | ||
61 | display = strtoul(display_str+1, &endp, 10); | ||
62 | // handling DISPLAY=:0 and also :0.0 | ||
63 | if (endp == display_str+1 || (*endp != '\0' && *endp != '.')) { | ||
64 | if (arg_debug) | ||
65 | fprintf(stderr, "unsupported DISPLAY form '%s'\n", display_str); | ||
66 | return -1; | ||
67 | } | ||
68 | if (errno || display > (unsigned long)INT_MAX) { | ||
69 | if (arg_debug) | ||
70 | fprintf(stderr, "display number %s is outside the valid range\n", | ||
71 | display_str+1); | ||
72 | return -1; | ||
73 | } | ||
74 | |||
75 | if (arg_debug) | ||
76 | fprintf(stderr, "DISPLAY=%s parsed as %lu\n", display_str, display); | ||
77 | |||
78 | return (int)display; | ||
79 | } | ||
80 | |||
81 | |||
82 | #ifdef HAVE_X11 | ||
83 | // check for X11 abstract sockets | ||
84 | static int x11_abstract_sockets_present(void) { | ||
85 | |||
86 | EUID_ROOT(); // grsecurity fix | ||
87 | FILE *fp = fopen("/proc/net/unix", "r"); | ||
88 | if (!fp) | ||
89 | errExit("fopen"); | ||
90 | EUID_USER(); | ||
91 | |||
92 | char *linebuf = 0; | ||
93 | size_t bufsz = 0; | ||
94 | int found = 0; | ||
95 | errno = 0; | ||
96 | |||
97 | for (;;) { | ||
98 | if (getline(&linebuf, &bufsz, fp) == -1) { | ||
99 | if (errno) | ||
100 | errExit("getline"); | ||
101 | break; | ||
102 | } | ||
103 | // The last space-separated field in 'linebuf' is the | ||
104 | // pathname of the socket. Abstract sockets' pathnames | ||
105 | // all begin with '@/', normal ones begin with '/'. | ||
106 | char *p = strrchr(linebuf, ' '); | ||
107 | if (!p) { | ||
108 | fputs("error parsing /proc/net/unix\n", stderr); | ||
109 | exit(1); | ||
110 | } | ||
111 | if (strncmp(p+1, "@/tmp/.X11-unix/", 16) == 0) { | ||
112 | found = 1; | ||
113 | break; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | free(linebuf); | ||
118 | fclose(fp); | ||
119 | return found; | ||
120 | } | ||
121 | |||
122 | |||
123 | // Choose a random, unallocated display number. This has an inherent | ||
124 | // and unavoidable TOCTOU race, since we cannot create either the | ||
125 | // socket or a lockfile ourselves. | ||
126 | static int random_display_number(void) { | ||
127 | int display; | ||
128 | int found = 0; | ||
129 | int i; | ||
130 | |||
131 | struct sockaddr_un sa; | ||
132 | // The -1 here is because we need space to inject a | ||
133 | // leading nul byte. | ||
134 | int sun_pathmax = (int)(sizeof sa.sun_path - 1); | ||
135 | assert((size_t)sun_pathmax == sizeof sa.sun_path - 1); | ||
136 | int sun_pathlen; | ||
137 | |||
138 | int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); | ||
139 | if (sockfd == -1) | ||
140 | errExit("socket"); | ||
141 | |||
142 | for (i = 0; i < 100; i++) { | ||
143 | display = rand() % (X11_DISPLAY_END - X11_DISPLAY_START) + X11_DISPLAY_START; | ||
144 | |||
145 | // The display number might be claimed by a server listening | ||
146 | // in _either_ the normal or the abstract namespace; they | ||
147 | // don't necessarily do both. The easiest way to check is | ||
148 | // to try to connect, both ways. | ||
149 | memset(&sa, 0, sizeof sa); | ||
150 | sa.sun_family = AF_UNIX; | ||
151 | sun_pathlen = snprintf(sa.sun_path, sun_pathmax, | ||
152 | "/tmp/.X11-unix/X%d", display); | ||
153 | if (sun_pathlen >= sun_pathmax) { | ||
154 | fprintf(stderr, "sun_path too small for display :%d" | ||
155 | " (only %d bytes usable)\n", display, sun_pathmax); | ||
156 | exit(1); | ||
157 | } | ||
158 | |||
159 | if (connect(sockfd, (struct sockaddr *)&sa, | ||
160 | offsetof(struct sockaddr_un, sun_path) + sun_pathlen + 1) == 0) { | ||
161 | close(sockfd); | ||
162 | sockfd = socket(AF_UNIX, SOCK_STREAM, 0); | ||
163 | if (sockfd == -1) | ||
164 | errExit("socket"); | ||
165 | continue; | ||
166 | } | ||
167 | if (errno != ECONNREFUSED && errno != ENOENT) | ||
168 | errExit("connect"); | ||
169 | |||
170 | // Name not claimed in the normal namespace; now try it | ||
171 | // in the abstract namespace. Note that abstract-namespace | ||
172 | // names are NOT nul-terminated; they extend to the length | ||
173 | // specified as the third argument to 'connect'. | ||
174 | memmove(sa.sun_path + 1, sa.sun_path, sun_pathlen + 1); | ||
175 | sa.sun_path[0] = '\0'; | ||
176 | if (connect(sockfd, (struct sockaddr *)&sa, | ||
177 | offsetof(struct sockaddr_un, sun_path) + 1 + sun_pathlen) == 0) { | ||
178 | close(sockfd); | ||
179 | sockfd = socket(AF_UNIX, SOCK_STREAM, 0); | ||
180 | if (sockfd == -1) | ||
181 | errExit("socket"); | ||
182 | continue; | ||
183 | } | ||
184 | if (errno != ECONNREFUSED && errno != ENOENT) | ||
185 | errExit("connect"); | ||
186 | |||
187 | // This display number is unclaimed. Of course, it could | ||
188 | // be claimed before we get around to doing it... | ||
189 | found = 1; | ||
190 | break; | ||
191 | } | ||
192 | close(sockfd); | ||
193 | |||
194 | if (!found) { | ||
195 | fputs("Error: cannot find an unallocated X11 display number, " | ||
196 | "exiting...\n", stderr); | ||
197 | exit(1); | ||
198 | } | ||
199 | return display; | ||
200 | } | ||
201 | #endif | ||
202 | |||
203 | #ifdef HAVE_X11 | ||
204 | void x11_start_xvfb(int argc, char **argv) { | ||
205 | EUID_ASSERT(); | ||
206 | int i; | ||
207 | struct stat s; | ||
208 | pid_t jail = 0; | ||
209 | pid_t server = 0; | ||
210 | |||
211 | setenv("FIREJAIL_X11", "yes", 1); | ||
212 | |||
213 | // mever try to run X servers as root!!! | ||
214 | if (getuid() == 0) { | ||
215 | fprintf(stderr, "Error: X11 sandboxing is not available when running as root\n"); | ||
216 | exit(1); | ||
217 | } | ||
218 | drop_privs(0); | ||
219 | |||
220 | // check xvfb | ||
221 | if (!program_in_path("Xvfb")) { | ||
222 | fprintf(stderr, "\nError: Xvfb program was not found in /usr/bin directory, please install it:\n"); | ||
223 | fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xvfb\n"); | ||
224 | fprintf(stderr, " Arch: sudo pacman -S xorg-server-xvfb\n"); | ||
225 | exit(0); | ||
226 | } | ||
227 | |||
228 | int display = random_display_number(); | ||
229 | char *display_str; | ||
230 | if (asprintf(&display_str, ":%d", display) == -1) | ||
231 | errExit("asprintf"); | ||
232 | |||
233 | assert(xvfb_screen); | ||
234 | |||
235 | char *server_argv[256] = { // rest initialyzed to NULL | ||
236 | "Xvfb", display_str, "-screen", "0", xvfb_screen | ||
237 | }; | ||
238 | unsigned pos = 0; | ||
239 | while (server_argv[pos] != NULL) pos++; | ||
240 | assert(xvfb_extra_params); // should be "" if empty | ||
241 | |||
242 | // parse xvfb_extra_params | ||
243 | // very basic quoting support | ||
244 | char *temp = strdup(xvfb_extra_params); | ||
245 | if (*xvfb_extra_params != '\0') { | ||
246 | if (!temp) | ||
247 | errExit("strdup"); | ||
248 | bool dquote = false; | ||
249 | bool squote = false; | ||
250 | for (i = 0; i < (int) strlen(xvfb_extra_params); i++) { | ||
251 | if (temp[i] == '\"') { | ||
252 | dquote = !dquote; | ||
253 | // replace closing quote by \0 | ||
254 | if (dquote) temp[i] = '\0'; | ||
255 | } | ||
256 | if (temp[i] == '\'') { | ||
257 | squote = !squote; | ||
258 | // replace closing quote by \0 | ||
259 | if (squote) temp[i] = '\0'; | ||
260 | } | ||
261 | if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0'; | ||
262 | if (dquote && squote) { | ||
263 | fprintf(stderr, "Error: mixed quoting found while parsing xvfb_extra_params\n"); | ||
264 | exit(1); | ||
265 | } | ||
266 | } | ||
267 | if (dquote) { | ||
268 | fprintf(stderr, "Error: unclosed quote found while parsing xvfb_extra_params\n"); | ||
269 | exit(1); | ||
270 | } | ||
271 | |||
272 | server_argv[pos++] = temp; | ||
273 | for (i = 0; i < (int) strlen(xvfb_extra_params)-1; i++) { | ||
274 | if (pos >= (sizeof(server_argv)/sizeof(*server_argv)) - 2) { | ||
275 | fprintf(stderr, "Error: arg count limit exceeded while parsing xvfb_extra_params\n"); | ||
276 | exit(1); | ||
277 | } | ||
278 | if (temp[i] == '\0' && (temp[i+1] == '\"' || temp[i+1] == '\'')) server_argv[pos++] = temp + i + 2; | ||
279 | else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | server_argv[pos++] = NULL; | ||
284 | |||
285 | assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); // no overrun | ||
286 | assert(server_argv[pos-1] == NULL); // last element is null | ||
287 | |||
288 | if (arg_debug) { | ||
289 | size_t i = 0; | ||
290 | printf("\n*** Starting xvfb server:"); | ||
291 | while (server_argv[i]!=NULL) { | ||
292 | printf(" \"%s\"", server_argv[i]); | ||
293 | i++; | ||
294 | } | ||
295 | printf(" ***\n\n"); | ||
296 | } | ||
297 | |||
298 | // remove --x11 arg | ||
299 | char *jail_argv[argc+2]; | ||
300 | int j = 0; | ||
301 | for (i = 0; i < argc; i++) { | ||
302 | if (strncmp(argv[i], "--x11", 5) == 0) | ||
303 | continue; | ||
304 | jail_argv[j] = argv[i]; | ||
305 | j++; | ||
306 | } | ||
307 | jail_argv[j] = NULL; | ||
308 | |||
309 | assert(j < argc+2); // no overrun | ||
310 | |||
311 | if (arg_debug) { | ||
312 | size_t i = 0; | ||
313 | printf("\n*** Stating xvfb client:"); | ||
314 | while (jail_argv[i]!=NULL) { | ||
315 | printf(" \"%s\"", jail_argv[i]); | ||
316 | i++; | ||
317 | } | ||
318 | printf(" ***\n\n"); | ||
319 | } | ||
320 | |||
321 | server = fork(); | ||
322 | if (server < 0) | ||
323 | errExit("fork"); | ||
324 | if (server == 0) { | ||
325 | if (arg_debug) | ||
326 | printf("Starting xvfb...\n"); | ||
327 | |||
328 | // running without privileges - see drop_privs call above | ||
329 | assert(getenv("LD_PRELOAD") == NULL); | ||
330 | execvp(server_argv[0], server_argv); | ||
331 | perror("execvp"); | ||
332 | _exit(1); | ||
333 | } | ||
334 | |||
335 | if (arg_debug) | ||
336 | printf("xvfb server pid %d\n", server); | ||
337 | |||
338 | // check X11 socket | ||
339 | char *fname; | ||
340 | if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1) | ||
341 | errExit("asprintf"); | ||
342 | int n = 0; | ||
343 | // wait for x11 server to start | ||
344 | while (++n < 10) { | ||
345 | sleep(1); | ||
346 | if (stat(fname, &s) == 0) | ||
347 | break; | ||
348 | }; | ||
349 | |||
350 | if (n == 10) { | ||
351 | fprintf(stderr, "Error: failed to start xvfb\n"); | ||
352 | exit(1); | ||
353 | } | ||
354 | free(fname); | ||
355 | |||
356 | assert(display_str); | ||
357 | setenv("DISPLAY", display_str, 1); | ||
358 | // run attach command | ||
359 | jail = fork(); | ||
360 | if (jail < 0) | ||
361 | errExit("fork"); | ||
362 | if (jail == 0) { | ||
363 | fmessage("\n*** Attaching to Xvfb display %d ***\n\n", display); | ||
364 | |||
365 | // running without privileges - see drop_privs call above | ||
366 | assert(getenv("LD_PRELOAD") == NULL); | ||
367 | execvp(jail_argv[0], jail_argv); | ||
368 | perror("execvp"); | ||
369 | _exit(1); | ||
370 | } | ||
371 | |||
372 | // cleanup | ||
373 | free(display_str); | ||
374 | free(temp); | ||
375 | |||
376 | // wait for either server or jail termination | ||
377 | pid_t pid = wait(NULL); | ||
378 | |||
379 | // see which process terminated and kill other | ||
380 | if (pid == server) { | ||
381 | kill(jail, SIGTERM); | ||
382 | } | ||
383 | else if (pid == jail) { | ||
384 | kill(server, SIGTERM); | ||
385 | } | ||
386 | |||
387 | // without this closing Xephyr window may mess your terminal: | ||
388 | // "monitoring" process will release terminal before | ||
389 | // jail process ends and releases terminal | ||
390 | wait(NULL); // fulneral | ||
391 | |||
392 | exit(0); | ||
393 | } | ||
394 | |||
395 | |||
396 | static char *extract_setting(int argc, char **argv, const char *argument) { | ||
397 | int i; | ||
398 | int len = strlen(argument); | ||
399 | |||
400 | for (i = 1; i < argc; i++) { | ||
401 | if (strncmp(argv[i], argument, len) == 0) { | ||
402 | return argv[i] + len; | ||
403 | } | ||
404 | |||
405 | // detect end of firejail params | ||
406 | if (strcmp(argv[i], "--") == 0) | ||
407 | break; | ||
408 | if (strncmp(argv[i], "--", 2) != 0) | ||
409 | break; | ||
410 | } | ||
411 | |||
412 | return NULL; | ||
413 | } | ||
414 | |||
415 | |||
416 | //$ Xephyr -ac -br -noreset -screen 800x600 :22 & | ||
417 | //$ DISPLAY=:22 firejail --net=eth0 --blacklist=/tmp/.X11-unix/x0 firefox | ||
418 | void x11_start_xephyr(int argc, char **argv) { | ||
419 | EUID_ASSERT(); | ||
420 | int i; | ||
421 | struct stat s; | ||
422 | pid_t jail = 0; | ||
423 | pid_t server = 0; | ||
424 | |||
425 | // default xephyr screen can be overwriten by a --xephyr-screen= command line option | ||
426 | char *newscreen = extract_setting(argc, argv, "--xephyr-screen="); | ||
427 | if (newscreen) | ||
428 | xephyr_screen = newscreen; | ||
429 | |||
430 | setenv("FIREJAIL_X11", "yes", 1); | ||
431 | |||
432 | // unfortunately, xephyr does a number of weird things when started by root user!!! | ||
433 | if (getuid() == 0) { | ||
434 | fprintf(stderr, "Error: X11 sandboxing is not available when running as root\n"); | ||
435 | exit(1); | ||
436 | } | ||
437 | drop_privs(0); | ||
438 | |||
439 | // check xephyr | ||
440 | if (!program_in_path("Xephyr")) { | ||
441 | fprintf(stderr, "\nError: Xephyr program was not found in /usr/bin directory, please install it:\n"); | ||
442 | fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xserver-xephyr\n"); | ||
443 | fprintf(stderr, " Arch: sudo pacman -S xorg-server-xephyr\n"); | ||
444 | exit(0); | ||
445 | } | ||
446 | |||
447 | int display = random_display_number(); | ||
448 | char *display_str; | ||
449 | if (asprintf(&display_str, ":%d", display) == -1) | ||
450 | errExit("asprintf"); | ||
451 | |||
452 | assert(xephyr_screen); | ||
453 | char *server_argv[256] = { // rest initialyzed to NULL | ||
454 | "Xephyr", "-ac", "-br", "-noreset", "-screen", xephyr_screen | ||
455 | }; | ||
456 | unsigned pos = 0; | ||
457 | while (server_argv[pos] != NULL) pos++; | ||
458 | if (checkcfg(CFG_XEPHYR_WINDOW_TITLE)) { | ||
459 | server_argv[pos++] = "-title"; | ||
460 | server_argv[pos++] = "firejail x11 sandbox"; | ||
461 | } | ||
462 | |||
463 | assert(xephyr_extra_params); // should be "" if empty | ||
464 | |||
465 | // parse xephyr_extra_params | ||
466 | // very basic quoting support | ||
467 | char *temp = strdup(xephyr_extra_params); | ||
468 | if (*xephyr_extra_params != '\0') { | ||
469 | if (!temp) | ||
470 | errExit("strdup"); | ||
471 | bool dquote = false; | ||
472 | bool squote = false; | ||
473 | for (i = 0; i < (int) strlen(xephyr_extra_params); i++) { | ||
474 | if (temp[i] == '\"') { | ||
475 | dquote = !dquote; | ||
476 | // replace closing quote by \0 | ||
477 | if (dquote) temp[i] = '\0'; | ||
478 | } | ||
479 | if (temp[i] == '\'') { | ||
480 | squote = !squote; | ||
481 | // replace closing quote by \0 | ||
482 | if (squote) temp[i] = '\0'; | ||
483 | } | ||
484 | if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0'; | ||
485 | if (dquote && squote) { | ||
486 | fprintf(stderr, "Error: mixed quoting found while parsing xephyr_extra_params\n"); | ||
487 | exit(1); | ||
488 | } | ||
489 | } | ||
490 | if (dquote) { | ||
491 | fprintf(stderr, "Error: unclosed quote found while parsing xephyr_extra_params\n"); | ||
492 | exit(1); | ||
493 | } | ||
494 | |||
495 | server_argv[pos++] = temp; | ||
496 | for (i = 0; i < (int) strlen(xephyr_extra_params)-1; i++) { | ||
497 | if (pos >= (sizeof(server_argv)/sizeof(*server_argv)) - 2) { | ||
498 | fprintf(stderr, "Error: arg count limit exceeded while parsing xephyr_extra_params\n"); | ||
499 | exit(1); | ||
500 | } | ||
501 | if (temp[i] == '\0' && (temp[i+1] == '\"' || temp[i+1] == '\'')) { | ||
502 | server_argv[pos++] = temp + i + 2; | ||
503 | } | ||
504 | else if (temp[i] == '\0' && temp[i+1] != '\0') { | ||
505 | server_argv[pos++] = temp + i + 1; | ||
506 | } | ||
507 | } | ||
508 | } | ||
509 | |||
510 | server_argv[pos++] = display_str; | ||
511 | server_argv[pos++] = NULL; | ||
512 | |||
513 | // no overrun | ||
514 | assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); | ||
515 | assert(server_argv[pos-1] == NULL); // last element is null | ||
516 | |||
517 | { | ||
518 | size_t i = 0; | ||
519 | printf("\n*** Starting xephyr server:"); | ||
520 | while (server_argv[i]!=NULL) { | ||
521 | printf(" \"%s\"", server_argv[i]); | ||
522 | i++; | ||
523 | } | ||
524 | printf(" ***\n\n"); | ||
525 | } | ||
526 | |||
527 | // remove --x11 arg | ||
528 | char *jail_argv[argc+2]; | ||
529 | int j = 0; | ||
530 | for (i = 0; i < argc; i++) { | ||
531 | if (strncmp(argv[i], "--x11", 5) == 0) | ||
532 | continue; | ||
533 | jail_argv[j] = argv[i]; | ||
534 | j++; | ||
535 | } | ||
536 | jail_argv[j] = NULL; | ||
537 | |||
538 | assert(j < argc+2); // no overrun | ||
539 | |||
540 | if (arg_debug) { | ||
541 | size_t i = 0; | ||
542 | printf("*** Starting xephyr client:"); | ||
543 | while (jail_argv[i]!=NULL) { | ||
544 | printf(" \"%s\"", jail_argv[i]); | ||
545 | i++; | ||
546 | } | ||
547 | printf(" ***\n\n"); | ||
548 | } | ||
549 | |||
550 | server = fork(); | ||
551 | if (server < 0) | ||
552 | errExit("fork"); | ||
553 | if (server == 0) { | ||
554 | if (arg_debug) | ||
555 | printf("Starting xephyr...\n"); | ||
556 | |||
557 | // running without privileges - see drop_privs call above | ||
558 | assert(getenv("LD_PRELOAD") == NULL); | ||
559 | execvp(server_argv[0], server_argv); | ||
560 | perror("execvp"); | ||
561 | _exit(1); | ||
562 | } | ||
563 | |||
564 | if (arg_debug) | ||
565 | printf("xephyr server pid %d\n", server); | ||
566 | |||
567 | // check X11 socket | ||
568 | char *fname; | ||
569 | if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1) | ||
570 | errExit("asprintf"); | ||
571 | int n = 0; | ||
572 | // wait for x11 server to start | ||
573 | while (++n < 10) { | ||
574 | sleep(1); | ||
575 | if (stat(fname, &s) == 0) | ||
576 | break; | ||
577 | }; | ||
578 | |||
579 | if (n == 10) { | ||
580 | fprintf(stderr, "Error: failed to start xephyr\n"); | ||
581 | exit(1); | ||
582 | } | ||
583 | free(fname); | ||
584 | |||
585 | assert(display_str); | ||
586 | setenv("DISPLAY", display_str, 1); | ||
587 | // run attach command | ||
588 | jail = fork(); | ||
589 | if (jail < 0) | ||
590 | errExit("fork"); | ||
591 | if (jail == 0) { | ||
592 | if (!arg_quiet) | ||
593 | printf("\n*** Attaching to Xephyr display %d ***\n\n", display); | ||
594 | |||
595 | // running without privileges - see drop_privs call above | ||
596 | assert(getenv("LD_PRELOAD") == NULL); | ||
597 | execvp(jail_argv[0], jail_argv); | ||
598 | perror("execvp"); | ||
599 | _exit(1); | ||
600 | } | ||
601 | |||
602 | // cleanup | ||
603 | free(display_str); | ||
604 | free(temp); | ||
605 | |||
606 | // wait for either server or jail termination | ||
607 | pid_t pid = wait(NULL); | ||
608 | |||
609 | // see which process terminated and kill other | ||
610 | if (pid == server) { | ||
611 | kill(jail, SIGTERM); | ||
612 | } | ||
613 | else if (pid == jail) { | ||
614 | kill(server, SIGTERM); | ||
615 | } | ||
616 | |||
617 | // without this closing Xephyr window may mess your terminal: | ||
618 | // "monitoring" process will release terminal before | ||
619 | // jail process ends and releases terminal | ||
620 | wait(NULL); // fulneral | ||
621 | |||
622 | exit(0); | ||
623 | } | ||
624 | |||
625 | |||
626 | void x11_start_xpra_old(int argc, char **argv, int display, char *display_str) { | ||
627 | EUID_ASSERT(); | ||
628 | int i; | ||
629 | struct stat s; | ||
630 | pid_t client = 0; | ||
631 | pid_t server = 0; | ||
632 | |||
633 | // build the start command | ||
634 | char *server_argv[256] = { // rest initialyzed to NULL | ||
635 | "xpra", "start", display_str, "--no-daemon", | ||
636 | }; | ||
637 | unsigned pos = 0; | ||
638 | while (server_argv[pos] != NULL) pos++; | ||
639 | |||
640 | assert(xpra_extra_params); // should be "" if empty | ||
641 | |||
642 | // parse xephyr_extra_params | ||
643 | // very basic quoting support | ||
644 | char *temp = strdup(xpra_extra_params); | ||
645 | if (*xpra_extra_params != '\0') { | ||
646 | if (!temp) | ||
647 | errExit("strdup"); | ||
648 | bool dquote = false; | ||
649 | bool squote = false; | ||
650 | for (i = 0; i < (int) strlen(xpra_extra_params); i++) { | ||
651 | if (temp[i] == '\"') { | ||
652 | dquote = !dquote; | ||
653 | // replace closing quote by \0 | ||
654 | if (dquote) temp[i] = '\0'; | ||
655 | } | ||
656 | if (temp[i] == '\'') { | ||
657 | squote = !squote; | ||
658 | // replace closing quote by \0 | ||
659 | if (squote) temp[i] = '\0'; | ||
660 | } | ||
661 | if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0'; | ||
662 | if (dquote && squote) { | ||
663 | fprintf(stderr, "Error: mixed quoting found while parsing xpra_extra_params\n"); | ||
664 | exit(1); | ||
665 | } | ||
666 | } | ||
667 | if (dquote) { | ||
668 | fprintf(stderr, "Error: unclosed quote found while parsing xpra_extra_params\n"); | ||
669 | exit(1); | ||
670 | } | ||
671 | |||
672 | server_argv[pos++] = temp; | ||
673 | for (i = 0; i < (int) strlen(xpra_extra_params)-1; i++) { | ||
674 | if (pos >= (sizeof(server_argv)/sizeof(*server_argv)) - 2) { | ||
675 | fprintf(stderr, "Error: arg count limit exceeded while parsing xpra_extra_params\n"); | ||
676 | exit(1); | ||
677 | } | ||
678 | if (temp[i] == '\0' && (temp[i+1] == '\"' || temp[i+1] == '\'')) { | ||
679 | server_argv[pos++] = temp + i + 2; | ||
680 | } | ||
681 | else if (temp[i] == '\0' && temp[i+1] != '\0') { | ||
682 | server_argv[pos++] = temp + i + 1; | ||
683 | } | ||
684 | } | ||
685 | } | ||
686 | |||
687 | server_argv[pos++] = NULL; | ||
688 | |||
689 | // no overrun | ||
690 | assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); | ||
691 | assert(server_argv[pos-1] == NULL); // last element is null | ||
692 | |||
693 | if (arg_debug) { | ||
694 | size_t i = 0; | ||
695 | printf("\n*** Starting xpra server: "); | ||
696 | while (server_argv[i]!=NULL) { | ||
697 | printf(" \"%s\"", server_argv[i]); | ||
698 | i++; | ||
699 | } | ||
700 | printf(" ***\n\n"); | ||
701 | } | ||
702 | |||
703 | int fd_null = -1; | ||
704 | if (arg_quiet) { | ||
705 | fd_null = open("/dev/null", O_RDWR); | ||
706 | if (fd_null == -1) | ||
707 | errExit("open"); | ||
708 | } | ||
709 | |||
710 | // start | ||
711 | server = fork(); | ||
712 | if (server < 0) | ||
713 | errExit("fork"); | ||
714 | if (server == 0) { | ||
715 | if (arg_debug) | ||
716 | printf("Starting xpra...\n"); | ||
717 | |||
718 | if (arg_quiet && fd_null != -1) { | ||
719 | dup2(fd_null,0); | ||
720 | dup2(fd_null,1); | ||
721 | dup2(fd_null,2); | ||
722 | } | ||
723 | |||
724 | // running without privileges - see drop_privs call above | ||
725 | assert(getenv("LD_PRELOAD") == NULL); | ||
726 | execvp(server_argv[0], server_argv); | ||
727 | perror("execvp"); | ||
728 | _exit(1); | ||
729 | } | ||
730 | |||
731 | // add a small delay, on some systems it takes some time for the server to start | ||
732 | sleep(5); | ||
733 | |||
734 | // check X11 socket | ||
735 | char *fname; | ||
736 | if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1) | ||
737 | errExit("asprintf"); | ||
738 | int n = 0; | ||
739 | // wait for x11 server to start | ||
740 | while (++n < 10) { | ||
741 | sleep(1); | ||
742 | if (stat(fname, &s) == 0) | ||
743 | break; | ||
744 | } | ||
745 | |||
746 | if (n == 10) { | ||
747 | fprintf(stderr, "Error: failed to start xpra\n"); | ||
748 | exit(1); | ||
749 | } | ||
750 | free(fname); | ||
751 | |||
752 | // build attach command | ||
753 | char *attach_argv[] = { "xpra", "--title=\"firejail x11 sandbox\"", "attach", display_str, NULL }; | ||
754 | |||
755 | // run attach command | ||
756 | client = fork(); | ||
757 | if (client < 0) | ||
758 | errExit("fork"); | ||
759 | if (client == 0) { | ||
760 | if (arg_quiet && fd_null != -1) { | ||
761 | dup2(fd_null,0); | ||
762 | dup2(fd_null,1); | ||
763 | dup2(fd_null,2); | ||
764 | } | ||
765 | |||
766 | fmessage("\n*** Attaching to xpra display %d ***\n\n", display); | ||
767 | |||
768 | // running without privileges - see drop_privs call above | ||
769 | assert(getenv("LD_PRELOAD") == NULL); | ||
770 | execvp(attach_argv[0], attach_argv); | ||
771 | perror("execvp"); | ||
772 | _exit(1); | ||
773 | } | ||
774 | |||
775 | assert(display_str); | ||
776 | setenv("DISPLAY", display_str, 1); | ||
777 | |||
778 | // build jail command | ||
779 | char *firejail_argv[argc+2]; | ||
780 | pos = 0; | ||
781 | for (i = 0; i < argc; i++) { | ||
782 | if (strncmp(argv[i], "--x11", 5) == 0) | ||
783 | continue; | ||
784 | firejail_argv[pos] = argv[i]; | ||
785 | pos++; | ||
786 | } | ||
787 | firejail_argv[pos] = NULL; | ||
788 | |||
789 | assert((int) pos < (argc+2)); | ||
790 | assert(!firejail_argv[pos]); | ||
791 | |||
792 | // start jail | ||
793 | pid_t jail = fork(); | ||
794 | if (jail < 0) | ||
795 | errExit("fork"); | ||
796 | if (jail == 0) { | ||
797 | // running without privileges - see drop_privs call above | ||
798 | assert(getenv("LD_PRELOAD") == NULL); | ||
799 | if (firejail_argv[0]) // shut up llvm scan-build | ||
800 | execvp(firejail_argv[0], firejail_argv); | ||
801 | perror("execvp"); | ||
802 | exit(1); | ||
803 | } | ||
804 | |||
805 | fmessage("Xpra server pid %d, xpra client pid %d, jail %d\n", server, client, jail); | ||
806 | |||
807 | sleep(1); // adding a delay in order to let the server start | ||
808 | |||
809 | // wait for jail or server to end | ||
810 | while (1) { | ||
811 | pid_t pid = wait(NULL); | ||
812 | |||
813 | if (pid == jail) { | ||
814 | char *stop_argv[] = { "xpra", "stop", display_str, NULL }; | ||
815 | pid_t stop = fork(); | ||
816 | if (stop < 0) | ||
817 | errExit("fork"); | ||
818 | if (stop == 0) { | ||
819 | if (arg_quiet && fd_null != -1) { | ||
820 | dup2(fd_null,0); | ||
821 | dup2(fd_null,1); | ||
822 | dup2(fd_null,2); | ||
823 | } | ||
824 | // running without privileges - see drop_privs call above | ||
825 | assert(getenv("LD_PRELOAD") == NULL); | ||
826 | execvp(stop_argv[0], stop_argv); | ||
827 | perror("execvp"); | ||
828 | _exit(1); | ||
829 | } | ||
830 | |||
831 | // wait for xpra server to stop, 10 seconds limit | ||
832 | while (++n < 10) { | ||
833 | sleep(1); | ||
834 | pid = waitpid(server, NULL, WNOHANG); | ||
835 | if (pid == server) | ||
836 | break; | ||
837 | } | ||
838 | |||
839 | if (arg_debug) { | ||
840 | if (n == 10) | ||
841 | printf("failed to stop xpra server gratefully\n"); | ||
842 | else | ||
843 | printf("xpra server successfully stopped in %d secs\n", n); | ||
844 | } | ||
845 | |||
846 | // kill xpra server and xpra client | ||
847 | kill(client, SIGTERM); | ||
848 | kill(server, SIGTERM); | ||
849 | exit(0); | ||
850 | } | ||
851 | else if (pid == server) { | ||
852 | // kill firejail process | ||
853 | kill(jail, SIGTERM); | ||
854 | // kill xpra client (should die with server, but...) | ||
855 | kill(client, SIGTERM); | ||
856 | exit(0); | ||
857 | } | ||
858 | } | ||
859 | } | ||
860 | |||
861 | |||
862 | void x11_start_xpra_new(int argc, char **argv, char *display_str) { | ||
863 | EUID_ASSERT(); | ||
864 | int i; | ||
865 | pid_t server = 0; | ||
866 | |||
867 | // build the start command | ||
868 | char *server_argv[256] = { // rest initialyzed to NULL | ||
869 | "xpra", "start", display_str, "--daemon=no", "--attach=yes", "--exit-with-children=yes" | ||
870 | }; | ||
871 | unsigned spos = 0; | ||
872 | unsigned fpos = 0; | ||
873 | while (server_argv[spos] != NULL) spos++; | ||
874 | |||
875 | // build jail command | ||
876 | char *firejail_argv[argc+2]; | ||
877 | size_t total_length = 0; | ||
878 | for (i = 0; i < argc; i++) { | ||
879 | if (strncmp(argv[i], "--x11", 5) == 0) | ||
880 | continue; | ||
881 | firejail_argv[fpos] = argv[i]; | ||
882 | fpos++; | ||
883 | total_length += strlen(argv[i]); | ||
884 | } | ||
885 | |||
886 | char *start_child_prefix = "--start-child="; | ||
887 | char *start_child; | ||
888 | start_child = malloc(total_length + strlen(start_child_prefix) + fpos + 2); | ||
889 | if (start_child == NULL) { | ||
890 | fprintf(stderr, "Error: unable to allocate start_child to assemble command\n"); | ||
891 | exit(1); | ||
892 | } | ||
893 | |||
894 | strcpy(start_child,start_child_prefix); | ||
895 | for(i = 0; (unsigned) i < fpos; i++) { | ||
896 | strncat(start_child,firejail_argv[i],strlen(firejail_argv[i])); | ||
897 | if((unsigned) i != fpos - 1) | ||
898 | strncat(start_child," ",strlen(" ")); | ||
899 | } | ||
900 | |||
901 | server_argv[spos++] = start_child; | ||
902 | |||
903 | server_argv[spos++] = NULL; | ||
904 | firejail_argv[fpos] = NULL; | ||
905 | |||
906 | assert(xpra_extra_params); // should be "" if empty | ||
907 | |||
908 | // parse xephyr_extra_params | ||
909 | // very basic quoting support | ||
910 | char *temp = strdup(xpra_extra_params); | ||
911 | if (*xpra_extra_params != '\0') { | ||
912 | if (!temp) | ||
913 | errExit("strdup"); | ||
914 | bool dquote = false; | ||
915 | bool squote = false; | ||
916 | for (i = 0; i < (int) strlen(xpra_extra_params); i++) { | ||
917 | if (temp[i] == '\"') { | ||
918 | dquote = !dquote; | ||
919 | // replace closing quote by \0 | ||
920 | if (dquote) temp[i] = '\0'; | ||
921 | } | ||
922 | if (temp[i] == '\'') { | ||
923 | squote = !squote; | ||
924 | // replace closing quote by \0 | ||
925 | if (squote) temp[i] = '\0'; | ||
926 | } | ||
927 | if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0'; | ||
928 | if (dquote && squote) { | ||
929 | fprintf(stderr, "Error: mixed quoting found while parsing xpra_extra_params\n"); | ||
930 | exit(1); | ||
931 | } | ||
932 | } | ||
933 | if (dquote) { | ||
934 | fprintf(stderr, "Error: unclosed quote found while parsing xpra_extra_params\n"); | ||
935 | exit(1); | ||
936 | } | ||
937 | |||
938 | server_argv[spos++] = temp; | ||
939 | for (i = 0; i < (int) strlen(xpra_extra_params)-1; i++) { | ||
940 | if (spos >= (sizeof(server_argv)/sizeof(*server_argv)) - 2) { | ||
941 | fprintf(stderr, "Error: arg count limit exceeded while parsing xpra_extra_params\n"); | ||
942 | exit(1); | ||
943 | } | ||
944 | if (temp[i] == '\0' && (temp[i+1] == '\"' || temp[i+1] == '\'')) { | ||
945 | server_argv[spos++] = temp + i + 2; | ||
946 | } | ||
947 | else if (temp[i] == '\0' && temp[i+1] != '\0') { | ||
948 | server_argv[spos++] = temp + i + 1; | ||
949 | } | ||
950 | } | ||
951 | } | ||
952 | |||
953 | server_argv[spos++] = NULL; | ||
954 | |||
955 | assert((int) fpos < (argc+2)); | ||
956 | assert(!firejail_argv[fpos]); | ||
957 | // no overrun | ||
958 | assert(spos < (sizeof(server_argv)/sizeof(*server_argv))); | ||
959 | assert(server_argv[spos-1] == NULL); // last element is null | ||
960 | |||
961 | if (arg_debug) { | ||
962 | size_t i = 0; | ||
963 | printf("\n*** Starting xpra server: "); | ||
964 | while (server_argv[i]!=NULL) { | ||
965 | printf(" \"%s\"", server_argv[i]); | ||
966 | i++; | ||
967 | } | ||
968 | printf(" ***\n\n"); | ||
969 | } | ||
970 | |||
971 | int fd_null = -1; | ||
972 | if (arg_quiet) { | ||
973 | fd_null = open("/dev/null", O_RDWR); | ||
974 | if (fd_null == -1) | ||
975 | errExit("open"); | ||
976 | } | ||
977 | |||
978 | // start | ||
979 | server = fork(); | ||
980 | if (server < 0) | ||
981 | errExit("fork"); | ||
982 | if (server == 0) { | ||
983 | if (arg_debug) | ||
984 | printf("Starting xpra...\n"); | ||
985 | |||
986 | if (arg_quiet && fd_null != -1) { | ||
987 | dup2(fd_null,0); | ||
988 | dup2(fd_null,1); | ||
989 | dup2(fd_null,2); | ||
990 | } | ||
991 | |||
992 | // running without privileges - see drop_privs call above | ||
993 | assert(getenv("LD_PRELOAD") == NULL); | ||
994 | execvp(server_argv[0], server_argv); | ||
995 | perror("execvp"); | ||
996 | _exit(1); | ||
997 | } | ||
998 | |||
999 | // wait for server to end | ||
1000 | while (1) { | ||
1001 | pid_t pid = wait(NULL); | ||
1002 | if (pid == server) { | ||
1003 | free(start_child); | ||
1004 | exit(0); | ||
1005 | } | ||
1006 | } | ||
1007 | } | ||
1008 | |||
1009 | |||
1010 | void x11_start_xpra(int argc, char **argv) { | ||
1011 | EUID_ASSERT(); | ||
1012 | |||
1013 | setenv("FIREJAIL_X11", "yes", 1); | ||
1014 | |||
1015 | // unfortunately, xpra does a number of weird things when started by root user!!! | ||
1016 | if (getuid() == 0) { | ||
1017 | fprintf(stderr, "Error: X11 sandboxing is not available when running as root\n"); | ||
1018 | exit(1); | ||
1019 | } | ||
1020 | drop_privs(0); | ||
1021 | |||
1022 | // check xpra | ||
1023 | if (!program_in_path("xpra")) { | ||
1024 | fprintf(stderr, "\nError: Xpra program was not found in /usr/bin directory, please install it:\n"); | ||
1025 | fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xpra\n"); | ||
1026 | exit(0); | ||
1027 | } | ||
1028 | |||
1029 | int display = random_display_number(); | ||
1030 | char *display_str; | ||
1031 | if (asprintf(&display_str, ":%d", display) == -1) | ||
1032 | errExit("asprintf"); | ||
1033 | |||
1034 | if (checkcfg(CFG_XPRA_ATTACH)) | ||
1035 | x11_start_xpra_new(argc, argv, display_str); | ||
1036 | else | ||
1037 | x11_start_xpra_old(argc, argv, display, display_str); | ||
1038 | } | ||
1039 | |||
1040 | |||
1041 | void x11_start(int argc, char **argv) { | ||
1042 | EUID_ASSERT(); | ||
1043 | |||
1044 | // unfortunately, xpra does a number of weird things when started by root user!!! | ||
1045 | if (getuid() == 0) { | ||
1046 | fprintf(stderr, "Error: X11 sandboxing is not available when running as root\n"); | ||
1047 | exit(1); | ||
1048 | } | ||
1049 | |||
1050 | // check xpra | ||
1051 | if (program_in_path("xpra")) | ||
1052 | x11_start_xpra(argc, argv); | ||
1053 | else if (program_in_path("Xephyr")) | ||
1054 | x11_start_xephyr(argc, argv); | ||
1055 | else { | ||
1056 | fprintf(stderr, "\nError: Xpra or Xephyr not found in /usr/bin directory, please install one of them:\n"); | ||
1057 | fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xpra\n"); | ||
1058 | fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xserver-xephyr\n"); | ||
1059 | exit(0); | ||
1060 | } | ||
1061 | } | ||
1062 | #endif | ||
1063 | |||
1064 | // Porting notes: | ||
1065 | // | ||
1066 | // 1. merge #1100 from zackw: | ||
1067 | // Attempting to run xauth -f directly on a file in /run/firejail/mnt/ directory fails on Debian 8 | ||
1068 | // with this message: | ||
1069 | // xauth: timeout in locking authority file /run/firejail/mnt/sec.Xauthority-Qt5Mu4 | ||
1070 | // Failed to create untrusted X cookie: xauth: exit 1 | ||
1071 | // For this reason we run xauth on a file in a tmpfs filesystem mounted on /tmp. This was | ||
1072 | // a partial merge. | ||
1073 | // | ||
1074 | // 2. Since we cannot deal with the TOCTOU condition when mounting .Xauthority in user home | ||
1075 | // directory, we need to make sure /usr/bin/xauth executable is the real thing, and not | ||
1076 | // something picked up on $PATH. | ||
1077 | // | ||
1078 | // 3. If for any reason xauth command fails, we exit the sandbox. On Debian 8 this happens | ||
1079 | // when using a network namespace. Somehow, xauth tries to connect to the abstract socket, | ||
1080 | // and it fails because of the network namespace - it should try to connect to the regular | ||
1081 | // Unix socket! If we ignore the fail condition, the program will be started on X server without | ||
1082 | // the security extension loaded. | ||
1083 | void x11_xorg(void) { | ||
1084 | #ifdef HAVE_X11 | ||
1085 | |||
1086 | // check xauth utility is present in the system | ||
1087 | struct stat s; | ||
1088 | if (stat("/usr/bin/xauth", &s) == -1) { | ||
1089 | fprintf(stderr, "Error: xauth utility not found in /usr/bin. Please install it:\n" | ||
1090 | " Debian/Ubuntu/Mint: sudo apt-get install xauth\n"); | ||
1091 | exit(1); | ||
1092 | } | ||
1093 | if (s.st_uid != 0 && s.st_gid != 0) { | ||
1094 | fprintf(stderr, "Error: invalid /usr/bin/xauth executable\n"); | ||
1095 | exit(1); | ||
1096 | } | ||
1097 | |||
1098 | // get DISPLAY env | ||
1099 | char *display = getenv("DISPLAY"); | ||
1100 | if (!display) { | ||
1101 | fputs("Error: --x11=xorg requires an 'outer' X11 server to use.\n", stderr); | ||
1102 | exit(1); | ||
1103 | } | ||
1104 | |||
1105 | // temporarily mount a tempfs on top of /tmp directory | ||
1106 | if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) | ||
1107 | errExit("mounting /tmp"); | ||
1108 | |||
1109 | // create the temporary .Xauthority file | ||
1110 | if (arg_debug) | ||
1111 | printf("Generating a new .Xauthority file\n"); | ||
1112 | char tmpfname[] = "/tmp/.tmpXauth-XXXXXX"; | ||
1113 | int fd = mkstemp(tmpfname); | ||
1114 | if (fd == -1) { | ||
1115 | fprintf(stderr, "Error: cannot create .Xauthority file\n"); | ||
1116 | exit(1); | ||
1117 | } | ||
1118 | if (fchown(fd, getuid(), getgid()) == -1) | ||
1119 | errExit("chown"); | ||
1120 | close(fd); | ||
1121 | |||
1122 | pid_t child = fork(); | ||
1123 | if (child < 0) | ||
1124 | errExit("fork"); | ||
1125 | if (child == 0) { | ||
1126 | drop_privs(1); | ||
1127 | clearenv(); | ||
1128 | #ifdef HAVE_GCOV | ||
1129 | __gcov_flush(); | ||
1130 | #endif | ||
1131 | execlp("/usr/bin/xauth", "/usr/bin/xauth", "-v", "-f", tmpfname, | ||
1132 | "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL); | ||
1133 | |||
1134 | _exit(127); | ||
1135 | } | ||
1136 | |||
1137 | // wait for the xauth process to finish | ||
1138 | int status; | ||
1139 | if (waitpid(child, &status, 0) != child) | ||
1140 | errExit("waitpid"); | ||
1141 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { | ||
1142 | /* success */ | ||
1143 | } | ||
1144 | else if (WIFEXITED(status)) { | ||
1145 | fprintf(stderr, "Failed to create untrusted X cookie: xauth: exit %d\n", | ||
1146 | WEXITSTATUS(status)); | ||
1147 | exit(1); | ||
1148 | } | ||
1149 | else if (WIFSIGNALED(status)) { | ||
1150 | fprintf(stderr, "Failed to create untrusted X cookie: xauth: %s\n", | ||
1151 | strsignal(WTERMSIG(status))); | ||
1152 | exit(1); | ||
1153 | } | ||
1154 | else { | ||
1155 | fprintf(stderr, "Failed to create untrusted X cookie: " | ||
1156 | "xauth: un-decodable exit status %04x\n", status); | ||
1157 | exit(1); | ||
1158 | } | ||
1159 | |||
1160 | // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted | ||
1161 | // automatically when the sandbox is closed (rename doesn't work) | ||
1162 | // root needed | ||
1163 | if (copy_file(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) { | ||
1164 | fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); | ||
1165 | exit(1); | ||
1166 | } | ||
1167 | /* coverity[toctou] */ | ||
1168 | unlink(tmpfname); | ||
1169 | umount("/tmp"); | ||
1170 | |||
1171 | // Ensure there is already a file in the usual location, so that bind-mount below will work. | ||
1172 | char *dest; | ||
1173 | if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) | ||
1174 | errExit("asprintf"); | ||
1175 | if (lstat(dest, &s) == -1) | ||
1176 | touch_file_as_user(dest, getuid(), getgid(), 0600); | ||
1177 | |||
1178 | // get a file descriptor for .Xauthority | ||
1179 | fd = safe_fd(dest, O_PATH|O_NOFOLLOW|O_CLOEXEC); | ||
1180 | if (fd == -1) | ||
1181 | errExit("safe_fd"); | ||
1182 | // check if the actual mount destination is a user owned regular file | ||
1183 | if (fstat(fd, &s) == -1) | ||
1184 | errExit("fstat"); | ||
1185 | if (!S_ISREG(s.st_mode) || s.st_uid != getuid()) { | ||
1186 | if (S_ISLNK(s.st_mode)) | ||
1187 | fprintf(stderr, "Error: .Xauthority is a symbolic link\n"); | ||
1188 | else | ||
1189 | fprintf(stderr, "Error: .Xauthority is not a user owned regular file\n"); | ||
1190 | exit(1); | ||
1191 | } | ||
1192 | |||
1193 | // mount via the link in /proc/self/fd | ||
1194 | char *proc; | ||
1195 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
1196 | errExit("asprintf"); | ||
1197 | if (mount(RUN_XAUTHORITY_SEC_FILE, proc, "none", MS_BIND, "mode=0600") == -1) { | ||
1198 | fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); | ||
1199 | exit(1); | ||
1200 | } | ||
1201 | free(proc); | ||
1202 | close(fd); | ||
1203 | // check /proc/self/mountinfo to confirm the mount is ok | ||
1204 | MountData *mptr = get_last_mount(); | ||
1205 | if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) | ||
1206 | errLogExit("invalid .Xauthority mount"); | ||
1207 | |||
1208 | ASSERT_PERMS(dest, getuid(), getgid(), 0600); | ||
1209 | free(dest); | ||
1210 | #endif | ||
1211 | } | ||
1212 | |||
1213 | |||
1214 | void fs_x11(void) { | ||
1215 | #ifdef HAVE_X11 | ||
1216 | int display = x11_display(); | ||
1217 | if (display <= 0) | ||
1218 | return; | ||
1219 | |||
1220 | char *x11file; | ||
1221 | if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1) | ||
1222 | errExit("asprintf"); | ||
1223 | struct stat x11stat; | ||
1224 | if (stat(x11file, &x11stat) == -1 || !S_ISSOCK(x11stat.st_mode)) { | ||
1225 | free(x11file); | ||
1226 | return; | ||
1227 | } | ||
1228 | |||
1229 | if (arg_debug || arg_debug_whitelists) | ||
1230 | fprintf(stderr, "Masking all X11 sockets except %s\n", x11file); | ||
1231 | |||
1232 | // Move the real /tmp/.X11-unix to a scratch location | ||
1233 | // so we can still access x11file after we mount a | ||
1234 | // tmpfs over /tmp/.X11-unix. | ||
1235 | int rv = mkdir(RUN_WHITELIST_X11_DIR, 0700); | ||
1236 | if (rv == -1) | ||
1237 | errExit("mkdir"); | ||
1238 | if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 0700)) | ||
1239 | errExit("set_perms"); | ||
1240 | |||
1241 | if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, 0, MS_BIND|MS_REC, 0) < 0) | ||
1242 | errExit("mount bind"); | ||
1243 | |||
1244 | // This directory must be mode 1777, or Xlib will barf. | ||
1245 | if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs", | ||
1246 | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, | ||
1247 | "mode=1777,uid=0,gid=0") < 0) | ||
1248 | errExit("mounting tmpfs on /tmp/.X11-unix"); | ||
1249 | fs_logger("tmpfs /tmp/.X11-unix"); | ||
1250 | |||
1251 | // create an empty file which will have the desired socket bind-mounted over it | ||
1252 | int fd = open(x11file, O_RDWR|O_CREAT|O_EXCL, x11stat.st_mode & ~S_IFMT); | ||
1253 | if (fd < 0) | ||
1254 | errExit(x11file); | ||
1255 | if (fchown(fd, x11stat.st_uid, x11stat.st_gid)) | ||
1256 | errExit("fchown"); | ||
1257 | close(fd); | ||
1258 | |||
1259 | // do the mount | ||
1260 | char *wx11file; | ||
1261 | if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1) | ||
1262 | errExit("asprintf"); | ||
1263 | if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1264 | errExit("mount bind"); | ||
1265 | fs_logger2("whitelist", x11file); | ||
1266 | |||
1267 | free(x11file); | ||
1268 | free(wx11file); | ||
1269 | |||
1270 | // block access to RUN_WHITELIST_X11_DIR | ||
1271 | if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, 0, MS_BIND, 0) < 0) | ||
1272 | errExit("mount"); | ||
1273 | fs_logger2("blacklist", RUN_WHITELIST_X11_DIR); | ||
1274 | #endif | ||
1275 | } | ||
1276 | |||
1277 | |||
1278 | void x11_block(void) { | ||
1279 | #ifdef HAVE_X11 | ||
1280 | // check abstract socket presence and network namespace options | ||
1281 | if ((!arg_nonetwork && !cfg.bridge0.configured && !cfg.interface0.configured) | ||
1282 | && x11_abstract_sockets_present()) { | ||
1283 | fprintf(stderr, "ERROR: --x11=none specified, but abstract X11 socket still accessible.\n" | ||
1284 | "Additional setup required. To block abstract X11 socket you can either:\n" | ||
1285 | " * use network namespace in firejail (--net=none, --net=...)\n" | ||
1286 | " * add \"-nolisten local\" to xserver options\n" | ||
1287 | " (eg. to your display manager config, or /etc/X11/xinit/xserverrc)\n"); | ||
1288 | exit(1); | ||
1289 | } | ||
1290 | |||
1291 | // blacklist sockets | ||
1292 | profile_check_line("blacklist /tmp/.X11-unix", 0, NULL); | ||
1293 | profile_add(strdup("blacklist /tmp/.X11-unix")); | ||
1294 | |||
1295 | // blacklist .Xauthority | ||
1296 | profile_check_line("blacklist ${HOME}/.Xauthority", 0, NULL); | ||
1297 | profile_add(strdup("blacklist ${HOME}/.Xauthority")); | ||
1298 | char *xauthority = getenv("XAUTHORITY"); | ||
1299 | if (xauthority) { | ||
1300 | char *line; | ||
1301 | if (asprintf(&line, "blacklist %s", xauthority) == -1) | ||
1302 | errExit("asprintf"); | ||
1303 | profile_check_line(line, 0, NULL); | ||
1304 | profile_add(line); | ||
1305 | } | ||
1306 | |||
1307 | // clear environment | ||
1308 | env_store("DISPLAY", RMENV); | ||
1309 | env_store("XAUTHORITY", RMENV); | ||
1310 | #endif | ||
1311 | } | ||
diff --git a/src/fldd/Makefile.in b/src/fldd/Makefile.in deleted file mode 100644 index 5af37cfbd..000000000 --- a/src/fldd/Makefile.in +++ /dev/null | |||
@@ -1,14 +0,0 @@ | |||
1 | all: fldd | ||
2 | |||
3 | include ../common.mk | ||
4 | |||
5 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h ../include/ldd_utils.h | ||
6 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ | ||
7 | |||
8 | fldd: $(OBJS) ../lib/ldd_utils.o | ||
9 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/ldd_utils.o $(LIBS) $(EXTRA_LDFLAGS) | ||
10 | |||
11 | clean:; rm -f *.o fldd *.gcov *.gcda *.gcno | ||
12 | |||
13 | distclean: clean | ||
14 | rm -fr Makefile | ||
diff --git a/src/fldd/main.c b/src/fldd/main.c deleted file mode 100644 index 4658e82fb..000000000 --- a/src/fldd/main.c +++ /dev/null | |||
@@ -1,353 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 "../include/common.h" | ||
22 | #include "../include/ldd_utils.h" | ||
23 | |||
24 | #include <fcntl.h> | ||
25 | #include <sys/mman.h> | ||
26 | #include <sys/mount.h> | ||
27 | #include <sys/stat.h> | ||
28 | #include <sys/types.h> | ||
29 | #include <unistd.h> | ||
30 | #include <dirent.h> | ||
31 | |||
32 | |||
33 | static int arg_quiet = 0; | ||
34 | static void copy_libs_for_lib(const char *lib); | ||
35 | |||
36 | typedef struct storage_t { | ||
37 | struct storage_t *next; | ||
38 | const char *name; | ||
39 | } Storage; | ||
40 | static Storage *libs = NULL; | ||
41 | static Storage *lib_paths = NULL; | ||
42 | |||
43 | // return 1 if found | ||
44 | static int storage_find(Storage *ptr, const char *name) { | ||
45 | while (ptr) { | ||
46 | if (strcmp(ptr->name, name) == 0) | ||
47 | return 1; | ||
48 | ptr = ptr->next; | ||
49 | } | ||
50 | |||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | static void storage_add(Storage **head, const char *name) { | ||
55 | if (storage_find(*head, name)) | ||
56 | return; | ||
57 | |||
58 | Storage *s = malloc(sizeof(Storage)); | ||
59 | if (!s) | ||
60 | errExit("malloc"); | ||
61 | s->next = *head; | ||
62 | *head = s; | ||
63 | s->name = strdup(name); | ||
64 | if (!s->name) | ||
65 | errExit("strdup"); | ||
66 | } | ||
67 | |||
68 | |||
69 | static void storage_print(Storage *ptr, int fd) { | ||
70 | while (ptr) { | ||
71 | dprintf(fd, "%s\n", ptr->name); | ||
72 | ptr = ptr->next; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | static bool ptr_ok(const void *ptr, const void *base, const void *end, const char *name) { | ||
77 | bool r; | ||
78 | (void) name; | ||
79 | |||
80 | r = (ptr >= base && ptr < end); | ||
81 | return r; | ||
82 | } | ||
83 | |||
84 | |||
85 | static void parse_elf(const char *exe) { | ||
86 | int f; | ||
87 | f = open(exe, O_RDONLY); | ||
88 | if (f < 0) { | ||
89 | if (!arg_quiet) | ||
90 | fprintf(stderr, "Warning fldd: cannot open %s, skipping...\n", exe); | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | struct stat s; | ||
95 | char *base = NULL, *end; | ||
96 | if (fstat(f, &s) == -1) | ||
97 | goto error_close; | ||
98 | base = mmap(0, s.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, f, 0); | ||
99 | if (base == MAP_FAILED) | ||
100 | goto error_close; | ||
101 | |||
102 | end = base + s.st_size; | ||
103 | |||
104 | Elf_Ehdr *ebuf = (Elf_Ehdr *)base; | ||
105 | if (strncmp((const char *)ebuf->e_ident, ELFMAG, SELFMAG) != 0) { | ||
106 | if (!arg_quiet) | ||
107 | fprintf(stderr, "Warning fldd: %s is not an ELF executable or library\n", exe); | ||
108 | goto close; | ||
109 | } | ||
110 | //unsigned char elfclass = ebuf->e_ident[EI_CLASS]; | ||
111 | //if (elfclass == ELFCLASS32) | ||
112 | //printf("%s 32bit\n", exe); | ||
113 | //else if (elfclass == ELFCLASS64) | ||
114 | //printf("%s 64bit\n", exe); | ||
115 | |||
116 | |||
117 | Elf_Phdr *pbuf = (Elf_Phdr *)(base + sizeof(*ebuf)); | ||
118 | while (ebuf->e_phnum-- > 0 && ptr_ok(pbuf, base, end, "pbuf")) { | ||
119 | switch (pbuf->p_type) { | ||
120 | case PT_INTERP: | ||
121 | // dynamic loader ld-linux.so | ||
122 | if (!ptr_ok(base + pbuf->p_offset, base, end, "base + pbuf->p_offset")) | ||
123 | goto close; | ||
124 | |||
125 | storage_add(&libs, base + pbuf->p_offset); | ||
126 | break; | ||
127 | } | ||
128 | pbuf++; | ||
129 | } | ||
130 | |||
131 | Elf_Shdr *sbuf = (Elf_Shdr *)(base + ebuf->e_shoff); | ||
132 | if (!ptr_ok(sbuf, base, end, "sbuf")) | ||
133 | goto close; | ||
134 | |||
135 | // Find strings section | ||
136 | char *strbase = NULL; | ||
137 | int sections = ebuf->e_shnum; | ||
138 | while (sections-- > 0 && ptr_ok(sbuf, base, end, "sbuf")) { | ||
139 | if (sbuf->sh_type == SHT_STRTAB) { | ||
140 | strbase = base + sbuf->sh_offset; | ||
141 | if (!ptr_ok(strbase, base, end, "strbase")) | ||
142 | goto close; | ||
143 | break; | ||
144 | } | ||
145 | sbuf++; | ||
146 | } | ||
147 | if (strbase == NULL) | ||
148 | goto error_close; | ||
149 | |||
150 | // Find dynamic section | ||
151 | sections = ebuf->e_shnum; | ||
152 | while (sections-- > 0 && ptr_ok(sbuf, base, end, "sbuf")) { | ||
153 | // TODO: running fldd on large gui programs (fldd /usr/bin/transmission-qt) | ||
154 | // crash on accessing memory location sbuf->sh_type if sbuf->sh_type in the previous section was 0 (SHT_NULL) | ||
155 | // for now we just exit the while loop - this is probably incorrect | ||
156 | // printf("sbuf %p #%s#, sections %d, type %u\n", sbuf, exe, sections, sbuf->sh_type); | ||
157 | if (!ptr_ok(sbuf, base, end, "sbuf")) | ||
158 | goto close; | ||
159 | |||
160 | if (sbuf->sh_type == SHT_NULL) | ||
161 | break; | ||
162 | if (sbuf->sh_type == SHT_DYNAMIC) { | ||
163 | Elf_Dyn *dbuf = (Elf_Dyn *)(base + sbuf->sh_offset); | ||
164 | if (!ptr_ok(dbuf, base, end, "dbuf")) | ||
165 | goto close; | ||
166 | // Find DT_RPATH/DT_RUNPATH tags first | ||
167 | unsigned long size = sbuf->sh_size; | ||
168 | while (size >= sizeof(*dbuf) && ptr_ok(dbuf, base, end, "dbuf")) { | ||
169 | if (dbuf->d_tag == DT_RPATH || dbuf->d_tag == DT_RUNPATH) { | ||
170 | const char *searchpath = strbase + dbuf->d_un.d_ptr; | ||
171 | if (!ptr_ok(searchpath, base, end, "searchpath")) | ||
172 | goto close; | ||
173 | storage_add(&lib_paths, searchpath); | ||
174 | } | ||
175 | size -= sizeof(*dbuf); | ||
176 | dbuf++; | ||
177 | } | ||
178 | // Find DT_NEEDED tags | ||
179 | dbuf = (Elf_Dyn *)(base + sbuf->sh_offset); | ||
180 | size = sbuf->sh_size; | ||
181 | while (size >= sizeof(*dbuf) && ptr_ok(dbuf, base, end, "dbuf")) { | ||
182 | if (dbuf->d_tag == DT_NEEDED) { | ||
183 | const char *lib = strbase + dbuf->d_un.d_ptr; | ||
184 | if (!ptr_ok(lib, base, end, "lib")) | ||
185 | goto close; | ||
186 | copy_libs_for_lib(lib); | ||
187 | } | ||
188 | size -= sizeof(*dbuf); | ||
189 | dbuf++; | ||
190 | } | ||
191 | } | ||
192 | sbuf++; | ||
193 | } | ||
194 | goto close; | ||
195 | |||
196 | error_close: | ||
197 | perror("copy libs"); | ||
198 | close: | ||
199 | if (base) | ||
200 | munmap(base, s.st_size); | ||
201 | |||
202 | close(f); | ||
203 | } | ||
204 | |||
205 | static void copy_libs_for_lib(const char *lib) { | ||
206 | Storage *lib_path; | ||
207 | for (lib_path = lib_paths; lib_path; lib_path = lib_path->next) { | ||
208 | char *fname; | ||
209 | if (asprintf(&fname, "%s/%s", lib_path->name, lib) == -1) | ||
210 | errExit("asprintf"); | ||
211 | if (access(fname, R_OK) == 0 && is_lib_64(fname)) { | ||
212 | if (!storage_find(libs, fname)) { | ||
213 | storage_add(&libs, fname); | ||
214 | // libs may need other libs | ||
215 | parse_elf(fname); | ||
216 | } | ||
217 | free(fname); | ||
218 | return; | ||
219 | } | ||
220 | free(fname); | ||
221 | } | ||
222 | |||
223 | // log a warning and continue | ||
224 | if (!arg_quiet) | ||
225 | fprintf(stderr, "Warning fldd: cannot find %s, skipping...\n", lib); | ||
226 | } | ||
227 | |||
228 | static void lib_paths_init(void) { | ||
229 | int i; | ||
230 | for (i = 0; default_lib_paths[i]; i++) | ||
231 | storage_add(&lib_paths, default_lib_paths[i]); | ||
232 | } | ||
233 | |||
234 | |||
235 | static void walk_directory(const char *dirname) { | ||
236 | assert(dirname); | ||
237 | |||
238 | DIR *dir = opendir(dirname); | ||
239 | if (dir) { | ||
240 | struct dirent *entry; | ||
241 | while ((entry = readdir(dir)) != NULL) { | ||
242 | if (strcmp(entry->d_name, ".") == 0) | ||
243 | continue; | ||
244 | if (strcmp(entry->d_name, "..") == 0) | ||
245 | continue; | ||
246 | |||
247 | // build full path | ||
248 | char *path; | ||
249 | if (asprintf(&path, "%s/%s", dirname, entry->d_name) == -1) | ||
250 | errExit("asprintf"); | ||
251 | |||
252 | // check regular so library | ||
253 | char *ptr = strstr(entry->d_name, ".so"); | ||
254 | if (ptr && is_lib_64(path)) { | ||
255 | if (*(ptr + 3) == '\0' || *(ptr + 3) == '.') { | ||
256 | parse_elf(path); | ||
257 | free(path); | ||
258 | continue; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | // check directory | ||
263 | // entry->d_type field is supported in glibc since version 2.19 (Feb 2014) | ||
264 | // we'll use stat to check for directories | ||
265 | struct stat s; | ||
266 | if (stat(path, &s) == -1) | ||
267 | errExit("stat"); | ||
268 | if (S_ISDIR(s.st_mode)) | ||
269 | walk_directory(path); | ||
270 | } | ||
271 | closedir(dir); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | |||
276 | |||
277 | static void usage(void) { | ||
278 | printf("Usage: fldd program_or_directory [file]\n"); | ||
279 | printf("Print a list of libraries used by program or store it in the file.\n"); | ||
280 | printf("Print a list of libraries used by all .so files in a directory or store it in the file.\n"); | ||
281 | } | ||
282 | |||
283 | int main(int argc, char **argv) { | ||
284 | #if 0 | ||
285 | { | ||
286 | //system("cat /proc/self/status"); | ||
287 | int i; | ||
288 | for (i = 0; i < argc; i++) | ||
289 | printf("*%s* ", argv[i]); | ||
290 | printf("\n"); | ||
291 | } | ||
292 | #endif | ||
293 | if (argc < 2) { | ||
294 | fprintf(stderr, "Error fldd: invalid arguments\n"); | ||
295 | usage(); | ||
296 | exit(1); | ||
297 | } | ||
298 | |||
299 | |||
300 | if (strcmp(argv[1], "--help") == 0) { | ||
301 | usage(); | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | // check program access | ||
306 | if (access(argv[1], R_OK)) { | ||
307 | fprintf(stderr, "Error fldd: cannot access %s\n", argv[1]); | ||
308 | exit(1); | ||
309 | } | ||
310 | |||
311 | char *quiet = getenv("FIREJAIL_QUIET"); | ||
312 | if (quiet && strcmp(quiet, "yes") == 0) | ||
313 | arg_quiet = 1; | ||
314 | |||
315 | if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { | ||
316 | usage(); | ||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | int fd = STDOUT_FILENO; | ||
321 | // attempt to open the file | ||
322 | if (argc == 3) { | ||
323 | fd = open(argv[2], O_CREAT | O_TRUNC | O_WRONLY, 0644); | ||
324 | if (!fd) { | ||
325 | fprintf(stderr, "Error fldd: invalid arguments\n"); | ||
326 | usage(); | ||
327 | exit(1); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | // initialize local storage | ||
332 | lib_paths_init(); | ||
333 | |||
334 | // process files | ||
335 | struct stat s; | ||
336 | if (stat(argv[1], &s) == -1) | ||
337 | errExit("stat"); | ||
338 | if (S_ISDIR(s.st_mode)) | ||
339 | walk_directory(argv[1]); | ||
340 | else { | ||
341 | if (is_lib_64(argv[1])) | ||
342 | parse_elf(argv[1]); | ||
343 | else | ||
344 | fprintf(stderr, "Warning fldd: %s is not a 64bit program/library\n", argv[1]); | ||
345 | } | ||
346 | |||
347 | |||
348 | // print libraries and exit | ||
349 | storage_print(libs, fd); | ||
350 | if (argc == 3) | ||
351 | close(fd); | ||
352 | return 0; | ||
353 | } | ||