aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar startx2017 <vradu.startx@yandex.com>2018-07-27 12:56:41 -0400
committerLibravatar startx2017 <vradu.startx@yandex.com>2018-07-27 12:56:41 -0400
commit3cf75fe9a34c0bb579502b106649a1fc58d39f35 (patch)
tree3c696691a48205e6c73987f562bcc7a80aac69e4 /src
parentcompile time marker for LTS code (diff)
downloadfirejail-3cf75fe9a34c0bb579502b106649a1fc58d39f35.tar.gz
firejail-3cf75fe9a34c0bb579502b106649a1fc58d39f35.tar.zst
firejail-3cf75fe9a34c0bb579502b106649a1fc58d39f35.zip
phase 1
Diffstat (limited to 'src')
-rw-r--r--src/faudit/Makefile.in14
-rw-r--r--src/faudit/caps.c78
-rw-r--r--src/faudit/dbus.c92
-rw-r--r--src/faudit/dev.c47
-rw-r--r--src/faudit/faudit.h68
-rw-r--r--src/faudit/files.c75
-rw-r--r--src/faudit/main.c98
-rw-r--r--src/faudit/network.c101
-rw-r--r--src/faudit/pid.c99
-rw-r--r--src/faudit/seccomp.c101
-rw-r--r--src/faudit/syscall.c105
-rw-r--r--src/faudit/x11.c63
-rw-r--r--src/fbuilder/Makefile.in14
-rw-r--r--src/fbuilder/build_bin.c126
-rw-r--r--src/fbuilder/build_fs.c317
-rw-r--r--src/fbuilder/build_home.c199
-rw-r--r--src/fbuilder/build_profile.c170
-rw-r--r--src/fbuilder/build_seccomp.c192
-rw-r--r--src/fbuilder/fbuilder.h69
-rw-r--r--src/fbuilder/filedb.c79
-rw-r--r--src/fbuilder/main.c93
-rw-r--r--src/fbuilder/utils.c72
-rw-r--r--src/firejail/fs.c3
-rw-r--r--src/firejail/fs_bin.c309
-rw-r--r--src/firejail/fs_home.c2
-rw-r--r--src/firejail/fs_lib.c378
-rw-r--r--src/firejail/fs_lib2.c314
-rw-r--r--src/firejail/main.c23
-rw-r--r--src/firejail/profile.c8
-rw-r--r--src/firejail/sandbox.c13
-rw-r--r--src/firejail/x11.c1311
-rw-r--r--src/fldd/Makefile.in14
-rw-r--r--src/fldd/main.c353
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 @@
1all: faudit
2
3include ../common.mk
4
5%.o : %.c $(H_FILE_LIST)
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7
8faudit: $(OBJS)
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS)
10
11clean:; rm -f *.o faudit *.gcov *.gcda *.gcno
12
13distclean: 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
25static 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
47static 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
59void 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
25int 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
49void 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
23void 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
37extern char *prog;
38
39// pid.c
40void pid_test(void);
41
42// caps.c
43void caps_test(void);
44
45// seccomp.c
46void seccomp_test(void);
47
48// syscall.c
49void syscall_helper(int argc, char **argv);
50void syscall_run(const char *name);
51
52// files.c
53void files_test(void);
54
55// network.c
56void network_test(void);
57
58// dbus.c
59int check_unix(const char *sockfile);
60void dbus_test(void);
61
62// dev.c
63void dev_test(void);
64
65// x11.c
66void 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
24static char *username = NULL;
25static char *homedir = NULL;
26
27static 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
44void 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"
21char *prog;
22
23int 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
26static 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
50static 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
74void 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
97void 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
22void 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
23static 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
44void 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>
27extern int init_module(void *module_image, unsigned long len,
28 const char *param_values);
29extern int finit_module(int fd, const char *param_values,
30 int flags);
31extern int delete_module(const char *name, int flags);
32extern int pivot_root(const char *new_root, const char *put_old);
33
34void 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
91void 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
25void 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 @@
1all: fbuilder
2
3include ../common.mk
4
5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7
8fbuilder: $(OBJS)
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS)
10
11clean:; rm -f *.o fbuilder *.gcov *.gcda *.gcno
12
13distclean: 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
22static FileDB *bin_out = NULL;
23
24static 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
98void 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
24static 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
89static 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//*******************************************
113static FileDB *etc_out = NULL;
114
115static 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
128void 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//*******************************************
149static FileDB *var_out = NULL;
150static 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
163void 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//*******************************************
178static FileDB *share_out = NULL;
179static 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
200void 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//*******************************************
214static FileDB *tmp_out = NULL;
215static void tmp_callback(char *ptr) {
216 filedb_add(tmp_out, ptr);
217}
218
219void 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//*******************************************
243static 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
281static FileDB *dev_out = NULL;
282static 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
297void 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
23static FileDB *db_skip = NULL;
24static FileDB *db_out = NULL;
25
26static 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
50void 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
161void 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
28static 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
43static 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
59void 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
23void 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//***************************************
85int unix_s = 0;
86int inet = 0;
87int inet6 = 0;
88int netlink = 0;
89int packet = 0;
90static 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
145void 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
32extern int arg_debug;
33
34// build_profile.c
35void build_profile(int argc, char **argv, int index, FILE *fp);
36
37// build_seccomp.c
38void build_seccomp(const char *fname, FILE *fp);
39void build_protocol(const char *fname, FILE *fp);
40
41// build_fs.c
42void build_etc(const char *fname, FILE *fp);
43void build_var(const char *fname, FILE *fp);
44void build_tmp(const char *fname, FILE *fp);
45void build_dev(const char *fname, FILE *fp);
46void build_share(const char *fname, FILE *fp);
47
48// build_bin.c
49void build_bin(const char *fname, FILE *fp);
50
51// build_home.c
52void build_home(const char *fname, FILE *fp);
53
54// utils.c
55int is_dir(const char *fname);
56char *extract_dir(char *fname);
57
58// filedb.c
59typedef struct filedb_t {
60 struct filedb_t *next;
61 char *fname; // file name
62 int len; // length of file name
63} FileDB;
64
65FileDB *filedb_add(FileDB *head, const char *fname);
66FileDB *filedb_find(FileDB *head, const char *fname);
67void 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
23FileDB *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
52FileDB *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
72void 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"
21int arg_debug = 0;
22
23static void usage(void) {
24 printf("Firejail profile builder\n");
25 printf("Usage: firejail [--debug] --build[=profile-file] program-and-arguments\n");
26}
27
28int main(int argc, char **argv) {
29#if 0
30{
31system("cat /proc/self/status");
32int i;
33for (i = 0; i < argc; i++)
34 printf("*%s* ", argv[i]);
35printf("\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
25int 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
55char *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
745char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { 745char *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
1297void fs_private_tmp(void) { 1298void 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
28static int prog_cnt = 0;
29
30static 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
43static 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[]
100static 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
138static 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
151static 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
222static 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
269void 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
30extern void fslib_install_stdc(void);
31extern void fslib_install_system(void);
32
33static int lib_cnt = 0;
34static int dir_cnt = 0;
35
36static 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
52static 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
60void 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
98void 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
139void 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
176static 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
198static 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
248void 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
268static 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
309void 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
24extern void fslib_duplicate(const char *full_path);
25extern void fslib_copy_libs(const char *full_path);
26extern 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
34typedef struct liblist_t {
35 const char *name;
36 int len;
37} LibList;
38
39static 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
65static 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
80static 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
104void 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
130typedef 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
139SysLib 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
222void 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
828static void run_builder(int argc, char **argv) { 831static 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.
43int 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
84static 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.
126static 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
204void 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
396static 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
418void 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
626void 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
862void 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
1010void 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
1041void 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.
1083void 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
1214void 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
1278void 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 @@
1all: fldd
2
3include ../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
8fldd: $(OBJS) ../lib/ldd_utils.o
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/ldd_utils.o $(LIBS) $(EXTRA_LDFLAGS)
10
11clean:; rm -f *.o fldd *.gcov *.gcda *.gcno
12
13distclean: 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
33static int arg_quiet = 0;
34static void copy_libs_for_lib(const char *lib);
35
36typedef struct storage_t {
37 struct storage_t *next;
38 const char *name;
39} Storage;
40static Storage *libs = NULL;
41static Storage *lib_paths = NULL;
42
43// return 1 if found
44static 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
54static 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
69static void storage_print(Storage *ptr, int fd) {
70 while (ptr) {
71 dprintf(fd, "%s\n", ptr->name);
72 ptr = ptr->next;
73 }
74}
75
76static 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
85static 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
205static 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
228static 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
235static 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
277static 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
283int main(int argc, char **argv) {
284#if 0
285{
286//system("cat /proc/self/status");
287int i;
288for (i = 0; i < argc; i++)
289 printf("*%s* ", argv[i]);
290printf("\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}