diff options
Diffstat (limited to 'src/jailcheck')
-rw-r--r-- | src/jailcheck/Makefile.in | 17 | ||||
-rw-r--r-- | src/jailcheck/access.c | 143 | ||||
-rw-r--r-- | src/jailcheck/apparmor.c | 40 | ||||
-rw-r--r-- | src/jailcheck/jailcheck.h | 62 | ||||
-rw-r--r-- | src/jailcheck/main.c | 192 | ||||
-rw-r--r-- | src/jailcheck/noexec.c | 113 | ||||
-rw-r--r-- | src/jailcheck/seccomp.c | 47 | ||||
-rw-r--r-- | src/jailcheck/sysfiles.c | 88 | ||||
-rw-r--r-- | src/jailcheck/utils.c | 102 | ||||
-rw-r--r-- | src/jailcheck/virtual.c | 125 |
10 files changed, 929 insertions, 0 deletions
diff --git a/src/jailcheck/Makefile.in b/src/jailcheck/Makefile.in new file mode 100644 index 000000000..d218c1f90 --- /dev/null +++ b/src/jailcheck/Makefile.in | |||
@@ -0,0 +1,17 @@ | |||
1 | .PHONY: all | ||
2 | all: jailcheck | ||
3 | |||
4 | include ../common.mk | ||
5 | |||
6 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/pid.h | ||
7 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ | ||
8 | |||
9 | jailcheck: $(OBJS) | ||
10 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/pid.o $(LIBS) $(EXTRA_LDFLAGS) | ||
11 | |||
12 | .PHONY: clean | ||
13 | clean:; rm -fr *.o jailcheck *.gcov *.gcda *.gcno *.plist | ||
14 | |||
15 | .PHONY: distclean | ||
16 | distclean: clean | ||
17 | rm -fr Makefile | ||
diff --git a/src/jailcheck/access.c b/src/jailcheck/access.c new file mode 100644 index 000000000..c18d64a82 --- /dev/null +++ b/src/jailcheck/access.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2021 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 "jailcheck.h" | ||
21 | #include <dirent.h> | ||
22 | #include <sys/wait.h> | ||
23 | |||
24 | typedef struct { | ||
25 | char *tfile; | ||
26 | char *tdir; | ||
27 | } TestDir; | ||
28 | |||
29 | #define MAX_TEST_FILES 16 | ||
30 | TestDir td[MAX_TEST_FILES]; | ||
31 | static int files_cnt = 0; | ||
32 | |||
33 | void access_setup(const char *directory) { | ||
34 | // I am root! | ||
35 | assert(directory); | ||
36 | assert(user_home_dir); | ||
37 | |||
38 | if (files_cnt >= MAX_TEST_FILES) { | ||
39 | fprintf(stderr, "Error: maximum number of test directories exceded\n"); | ||
40 | exit(1); | ||
41 | } | ||
42 | |||
43 | char *fname = strdup(directory); | ||
44 | if (!fname) | ||
45 | errExit("strdup"); | ||
46 | if (strncmp(fname, "~/", 2) == 0) { | ||
47 | free(fname); | ||
48 | if (asprintf(&fname, "%s/%s", user_home_dir, directory + 2) == -1) | ||
49 | errExit("asprintf"); | ||
50 | } | ||
51 | |||
52 | char *path = realpath(fname, NULL); | ||
53 | free(fname); | ||
54 | if (path == NULL) { | ||
55 | fprintf(stderr, "Warning: invalid directory %s, skipping...\n", directory); | ||
56 | return; | ||
57 | } | ||
58 | |||
59 | // file in home directory | ||
60 | if (strncmp(path, user_home_dir, strlen(user_home_dir)) != 0) { | ||
61 | fprintf(stderr, "Warning: file %s is not in user home directory, skipping...\n", directory); | ||
62 | free(path); | ||
63 | return; | ||
64 | } | ||
65 | |||
66 | // try to open the dir as root | ||
67 | DIR *dir = opendir(path); | ||
68 | if (!dir) { | ||
69 | fprintf(stderr, "Warning: directory %s not found, skipping\n", directory); | ||
70 | free(path); | ||
71 | return; | ||
72 | } | ||
73 | closedir(dir); | ||
74 | |||
75 | // create a test file | ||
76 | char *test_file; | ||
77 | if (asprintf(&test_file, "%s/jailcheck-access-%d", path, getpid()) == -1) | ||
78 | errExit("asprintf"); | ||
79 | |||
80 | FILE *fp = fopen(test_file, "w"); | ||
81 | if (!fp) { | ||
82 | printf("Warning: I cannot create test file in directory %s, skipping...\n", directory); | ||
83 | return; | ||
84 | } | ||
85 | fprintf(fp, "this file was created by firetest utility, you can safely delete it\n"); | ||
86 | fclose(fp); | ||
87 | int rv = chown(test_file, user_uid, user_gid); | ||
88 | if (rv) | ||
89 | errExit("chown"); | ||
90 | |||
91 | char *dname = strdup(directory); | ||
92 | if (!dname) | ||
93 | errExit("strdup"); | ||
94 | td[files_cnt].tdir = dname; | ||
95 | td[files_cnt].tfile = test_file; | ||
96 | files_cnt++; | ||
97 | } | ||
98 | |||
99 | void access_destroy(void) { | ||
100 | // remove test files | ||
101 | int i; | ||
102 | |||
103 | for (i = 0; i < files_cnt; i++) { | ||
104 | int rv = unlink(td[i].tfile); | ||
105 | (void) rv; | ||
106 | } | ||
107 | files_cnt = 0; | ||
108 | } | ||
109 | |||
110 | void access_test(void) { | ||
111 | // I am root in sandbox mount namespace | ||
112 | assert(user_uid); | ||
113 | int i; | ||
114 | |||
115 | pid_t child = fork(); | ||
116 | if (child == -1) | ||
117 | errExit("fork"); | ||
118 | |||
119 | if (child == 0) { // child | ||
120 | // drop privileges | ||
121 | if (setgid(user_gid) != 0) | ||
122 | errExit("setgid"); | ||
123 | if (setuid(user_uid) != 0) | ||
124 | errExit("setuid"); | ||
125 | |||
126 | for (i = 0; i < files_cnt; i++) { | ||
127 | assert(td[i].tfile); | ||
128 | |||
129 | // try to open the file for reading | ||
130 | FILE *fp = fopen(td[i].tfile, "r"); | ||
131 | if (fp) { | ||
132 | |||
133 | printf(" Warning: I can read %s\n", td[i].tdir); | ||
134 | fclose(fp); | ||
135 | } | ||
136 | } | ||
137 | exit(0); | ||
138 | } | ||
139 | |||
140 | // wait for the child to finish | ||
141 | int status; | ||
142 | wait(&status); | ||
143 | } | ||
diff --git a/src/jailcheck/apparmor.c b/src/jailcheck/apparmor.c new file mode 100644 index 000000000..64f278046 --- /dev/null +++ b/src/jailcheck/apparmor.c | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2021 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 "jailcheck.h" | ||
21 | |||
22 | #ifdef HAVE_APPARMOR | ||
23 | #include <sys/apparmor.h> | ||
24 | |||
25 | void apparmor_test(pid_t pid) { | ||
26 | char *label = NULL; | ||
27 | char *mode = NULL; | ||
28 | int rv = aa_gettaskcon(pid, &label, &mode); | ||
29 | if (rv == -1 || mode == NULL) | ||
30 | printf(" Warning: AppArmor not enabled\n"); | ||
31 | } | ||
32 | |||
33 | |||
34 | #else | ||
35 | void apparmor_test(pid_t pid) { | ||
36 | (void) pid; | ||
37 | return; | ||
38 | } | ||
39 | #endif | ||
40 | |||
diff --git a/src/jailcheck/jailcheck.h b/src/jailcheck/jailcheck.h new file mode 100644 index 000000000..32be1c978 --- /dev/null +++ b/src/jailcheck/jailcheck.h | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2021 Firejail Authors | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | #ifndef JAILCHECK_H | ||
21 | #define JAILCHECK_H | ||
22 | |||
23 | #include "../include/common.h" | ||
24 | |||
25 | // main.c | ||
26 | extern uid_t user_uid; | ||
27 | extern gid_t user_gid; | ||
28 | extern char *user_name; | ||
29 | extern char *user_home_dir; | ||
30 | extern char *user_run_dir; | ||
31 | |||
32 | // access.c | ||
33 | void access_setup(const char *directory); | ||
34 | void access_test(void); | ||
35 | void access_destroy(void); | ||
36 | |||
37 | // noexec.c | ||
38 | void noexec_setup(void); | ||
39 | void noexec_test(const char *msg); | ||
40 | |||
41 | // sysfiles.c | ||
42 | void sysfiles_setup(const char *file); | ||
43 | void sysfiles_test(void); | ||
44 | |||
45 | // virtual.c | ||
46 | void virtual_setup(const char *directory); | ||
47 | void virtual_destroy(void); | ||
48 | void virtual_test(void); | ||
49 | |||
50 | // apparmor.c | ||
51 | void apparmor_test(pid_t pid); | ||
52 | |||
53 | // seccomp.c | ||
54 | void seccomp_test(pid_t pid); | ||
55 | |||
56 | // utils.c | ||
57 | char *get_sudo_user(void); | ||
58 | char *get_homedir(const char *user, uid_t *uid, gid_t *gid); | ||
59 | int find_child(pid_t pid); | ||
60 | pid_t switch_to_child(pid_t pid); | ||
61 | |||
62 | #endif \ No newline at end of file | ||
diff --git a/src/jailcheck/main.c b/src/jailcheck/main.c new file mode 100644 index 000000000..4d642bf96 --- /dev/null +++ b/src/jailcheck/main.c | |||
@@ -0,0 +1,192 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2021 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 "jailcheck.h" | ||
21 | #include "../include/firejail_user.h" | ||
22 | #include "../include/pid.h" | ||
23 | #include <sys/wait.h> | ||
24 | |||
25 | uid_t user_uid = 0; | ||
26 | gid_t user_gid = 0; | ||
27 | char *user_name = NULL; | ||
28 | char *user_home_dir = NULL; | ||
29 | char *user_run_dir = NULL; | ||
30 | int arg_debug = 0; | ||
31 | |||
32 | static char *usage_str = | ||
33 | "Usage: jailcheck [options] directory [directory]\n\n" | ||
34 | "Options:\n" | ||
35 | " --debug - print debug messages.\n" | ||
36 | " --help, -? - this help screen.\n" | ||
37 | " --version - print program version and exit.\n"; | ||
38 | |||
39 | |||
40 | static void usage(void) { | ||
41 | printf("firetest - version %s\n\n", VERSION); | ||
42 | puts(usage_str); | ||
43 | } | ||
44 | |||
45 | static void cleanup(void) { | ||
46 | // running only as root | ||
47 | if (getuid() == 0) { | ||
48 | if (arg_debug) | ||
49 | printf("cleaning up!\n"); | ||
50 | access_destroy(); | ||
51 | virtual_destroy(); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | int main(int argc, char **argv) { | ||
56 | int i; | ||
57 | int findex = 0; | ||
58 | |||
59 | for (i = 1; i < argc; i++) { | ||
60 | if (strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "--help") == 0) { | ||
61 | usage(); | ||
62 | return 0; | ||
63 | } | ||
64 | else if (strcmp(argv[i], "--version") == 0) { | ||
65 | printf("firetest version %s\n\n", VERSION); | ||
66 | return 0; | ||
67 | } | ||
68 | else if (strncmp(argv[i], "--hello=", 8) == 0) { // used by noexec test | ||
69 | printf(" Warning: I can run programs in %s\n", argv[i] + 8); | ||
70 | return 0; | ||
71 | } | ||
72 | else if (strcmp(argv[i], "--debug") == 0) | ||
73 | arg_debug = 1; | ||
74 | else if (strncmp(argv[i], "--", 2) == 0) { | ||
75 | fprintf(stderr, "Error: invalid option\n"); | ||
76 | return 1; | ||
77 | } | ||
78 | else { | ||
79 | findex = i; | ||
80 | break; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | // user setup | ||
85 | if (getuid() != 0) { | ||
86 | fprintf(stderr, "Error: you need to be root (via sudo) to run this program\n"); | ||
87 | exit(1); | ||
88 | } | ||
89 | user_name = get_sudo_user(); | ||
90 | assert(user_name); | ||
91 | user_home_dir = get_homedir(user_name, &user_uid, &user_gid); | ||
92 | if (user_uid == 0) { | ||
93 | fprintf(stderr, "Error: root user not supported\n"); | ||
94 | exit(1); | ||
95 | } | ||
96 | if (asprintf(&user_run_dir, "/run/user/%d", user_uid) == -1) | ||
97 | errExit("asprintf"); | ||
98 | |||
99 | // test setup | ||
100 | atexit(cleanup); | ||
101 | access_setup("~/.ssh"); | ||
102 | access_setup("~/.gnupg"); | ||
103 | if (findex > 0) { | ||
104 | for (i = findex; i < argc; i++) | ||
105 | access_setup(argv[i]); | ||
106 | } | ||
107 | |||
108 | noexec_setup(); | ||
109 | virtual_setup(user_home_dir); | ||
110 | virtual_setup("/tmp"); | ||
111 | virtual_setup("/var/tmp"); | ||
112 | virtual_setup("/dev"); | ||
113 | virtual_setup("/etc"); | ||
114 | virtual_setup("/bin"); | ||
115 | virtual_setup("/usr/share"); | ||
116 | virtual_setup(user_run_dir); | ||
117 | // basic sysfiles | ||
118 | sysfiles_setup("/etc/shadow"); | ||
119 | sysfiles_setup("/etc/gshadow"); | ||
120 | sysfiles_setup("/usr/bin/mount"); | ||
121 | sysfiles_setup("/usr/bin/su"); | ||
122 | sysfiles_setup("/usr/bin/ksu"); | ||
123 | sysfiles_setup("/usr/bin/sudo"); | ||
124 | sysfiles_setup("/usr/bin/strace"); | ||
125 | // X11 | ||
126 | sysfiles_setup("/usr/bin/xev"); | ||
127 | sysfiles_setup("/usr/bin/xinput"); | ||
128 | // compilers | ||
129 | sysfiles_setup("/usr/bin/gcc"); | ||
130 | sysfiles_setup("/usr/bin/clang"); | ||
131 | // networking | ||
132 | sysfiles_setup("/usr/bin/dig"); | ||
133 | sysfiles_setup("/usr/bin/nslookup"); | ||
134 | sysfiles_setup("/usr/bin/resolvectl"); | ||
135 | sysfiles_setup("/usr/bin/nc"); | ||
136 | sysfiles_setup("/usr/bin/ncat"); | ||
137 | sysfiles_setup("/usr/bin/nmap"); | ||
138 | sysfiles_setup("/usr/sbin/tcpdump"); | ||
139 | // terminals | ||
140 | sysfiles_setup("/usr/bin/gnome-terminal"); | ||
141 | sysfiles_setup("/usr/bin/xfce4-terminal"); | ||
142 | sysfiles_setup("/usr/bin/lxterminal"); | ||
143 | |||
144 | // print processes | ||
145 | pid_read(0); | ||
146 | for (i = 0; i < max_pids; i++) { | ||
147 | if (pids[i].level == 1) { | ||
148 | uid_t uid = pid_get_uid(i); | ||
149 | if (uid != user_uid) // not interested in other user sandboxes | ||
150 | continue; | ||
151 | |||
152 | // in case the pid is that of a firejail process, use the pid of the first child process | ||
153 | uid_t pid = find_child(i); | ||
154 | printf("\n"); | ||
155 | pid_print_list(i, 0); // no wrapping | ||
156 | apparmor_test(pid); | ||
157 | seccomp_test(pid); | ||
158 | fflush(0); | ||
159 | |||
160 | pid_t child = fork(); | ||
161 | if (child == -1) | ||
162 | errExit("fork"); | ||
163 | if (child == 0) { | ||
164 | int rv = join_namespace(pid, "mnt"); | ||
165 | if (rv == 0) { | ||
166 | virtual_test(); | ||
167 | noexec_test(user_home_dir); | ||
168 | noexec_test("/tmp"); | ||
169 | noexec_test("/var/tmp"); | ||
170 | noexec_test(user_run_dir); | ||
171 | access_test(); | ||
172 | sysfiles_test(); | ||
173 | } | ||
174 | else { | ||
175 | printf(" Error: I cannot join the process mount space\n"); | ||
176 | exit(1); | ||
177 | } | ||
178 | |||
179 | // drop privileges in order not to trigger cleanup() | ||
180 | if (setgid(user_gid) != 0) | ||
181 | errExit("setgid"); | ||
182 | if (setuid(user_uid) != 0) | ||
183 | errExit("setuid"); | ||
184 | return 0; | ||
185 | } | ||
186 | int status; | ||
187 | wait(&status); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | return 0; | ||
192 | } | ||
diff --git a/src/jailcheck/noexec.c b/src/jailcheck/noexec.c new file mode 100644 index 000000000..7f994d6a1 --- /dev/null +++ b/src/jailcheck/noexec.c | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2021 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 "jailcheck.h" | ||
21 | #include <sys/wait.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <fcntl.h> | ||
24 | |||
25 | static unsigned char *execfile = NULL; | ||
26 | static int execfile_len = 0; | ||
27 | |||
28 | void noexec_setup(void) { | ||
29 | // grab a copy of myself | ||
30 | char *self = realpath("/proc/self/exe", NULL); | ||
31 | if (self) { | ||
32 | struct stat s; | ||
33 | if (access(self, X_OK) == 0 && stat(self, &s) == 0) { | ||
34 | assert(s.st_size); | ||
35 | execfile = malloc(s.st_size); | ||
36 | |||
37 | int fd = open(self, O_RDONLY); | ||
38 | if (fd == -1) | ||
39 | errExit("open"); | ||
40 | int len = 0; | ||
41 | do { | ||
42 | int rv = read(fd, execfile + len, s.st_size - len); | ||
43 | if (rv == -1) | ||
44 | errExit("read"); | ||
45 | if (rv == 0) { | ||
46 | // something went wrong! | ||
47 | free(execfile); | ||
48 | execfile = NULL; | ||
49 | printf("Warning: I cannot grab a copy of myself, skipping noexec test...\n"); | ||
50 | break; | ||
51 | } | ||
52 | len += rv; | ||
53 | } | ||
54 | while (len < s.st_size); | ||
55 | execfile_len = s.st_size; | ||
56 | close(fd); | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | |||
62 | void noexec_test(const char *path) { | ||
63 | assert(user_uid); | ||
64 | |||
65 | // I am root in sandbox mount namespace | ||
66 | if (!execfile) | ||
67 | return; | ||
68 | |||
69 | char *fname; | ||
70 | if (asprintf(&fname, "%s/jailcheck-noexec-%d", path, getpid()) == -1) | ||
71 | errExit("asprintf"); | ||
72 | |||
73 | pid_t child = fork(); | ||
74 | if (child == -1) | ||
75 | errExit("fork"); | ||
76 | |||
77 | if (child == 0) { // child | ||
78 | // drop privileges | ||
79 | if (setgid(user_gid) != 0) | ||
80 | errExit("setgid"); | ||
81 | if (setuid(user_uid) != 0) | ||
82 | errExit("setuid"); | ||
83 | int fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0700); | ||
84 | if (fd == -1) { | ||
85 | printf(" I cannot create files in %s, skipping noexec...\n", path); | ||
86 | exit(1); | ||
87 | } | ||
88 | |||
89 | int len = 0; | ||
90 | while (len < execfile_len) { | ||
91 | int rv = write(fd, execfile + len, execfile_len - len); | ||
92 | if (rv == -1 || rv == 0) { | ||
93 | printf(" I cannot create files in %s, skipping noexec....\n", path); | ||
94 | exit(1); | ||
95 | } | ||
96 | len += rv; | ||
97 | } | ||
98 | fchmod(fd, 0700); | ||
99 | close(fd); | ||
100 | |||
101 | char *arg; | ||
102 | if (asprintf(&arg, "--hello=%s", path) == -1) | ||
103 | errExit("asprintf"); | ||
104 | int rv = execl(fname, fname, arg, NULL); | ||
105 | (void) rv; // if we get here execl failed | ||
106 | exit(0); | ||
107 | } | ||
108 | |||
109 | int status; | ||
110 | wait(&status); | ||
111 | int rv = unlink(fname); | ||
112 | (void) rv; | ||
113 | } \ No newline at end of file | ||
diff --git a/src/jailcheck/seccomp.c b/src/jailcheck/seccomp.c new file mode 100644 index 000000000..9345eb970 --- /dev/null +++ b/src/jailcheck/seccomp.c | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2021 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 "jailcheck.h" | ||
21 | #define MAXBUF 4096 | ||
22 | |||
23 | void seccomp_test(pid_t pid) { | ||
24 | char *file; | ||
25 | if (asprintf(&file, "/proc/%d/status", pid) == -1) | ||
26 | errExit("asprintf"); | ||
27 | |||
28 | FILE *fp = fopen(file, "r"); | ||
29 | if (!fp) { | ||
30 | printf(" Error: cannot open %s\n", file); | ||
31 | free(file); | ||
32 | return; | ||
33 | } | ||
34 | |||
35 | char buf[MAXBUF]; | ||
36 | while (fgets(buf, MAXBUF, fp)) { | ||
37 | if (strncmp(buf, "Seccomp:", 8) == 0) { | ||
38 | int val = -1; | ||
39 | int rv = sscanf(buf + 8, "\t%d", &val); | ||
40 | if (rv != 1 || val == 0) | ||
41 | printf(" Warning: seccomp not enabled\n"); | ||
42 | break; | ||
43 | } | ||
44 | } | ||
45 | fclose(fp); | ||
46 | free(file); | ||
47 | } | ||
diff --git a/src/jailcheck/sysfiles.c b/src/jailcheck/sysfiles.c new file mode 100644 index 000000000..caeb580af --- /dev/null +++ b/src/jailcheck/sysfiles.c | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2021 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 "jailcheck.h" | ||
21 | #include <dirent.h> | ||
22 | #include <sys/wait.h> | ||
23 | |||
24 | typedef struct { | ||
25 | char *tfile; | ||
26 | } TestFile; | ||
27 | |||
28 | #define MAX_TEST_FILES 32 | ||
29 | TestFile tf[MAX_TEST_FILES]; | ||
30 | static int files_cnt = 0; | ||
31 | |||
32 | void sysfiles_setup(const char *file) { | ||
33 | // I am root! | ||
34 | assert(file); | ||
35 | |||
36 | if (files_cnt >= MAX_TEST_FILES) { | ||
37 | fprintf(stderr, "Error: maximum number of system test files exceded\n"); | ||
38 | exit(1); | ||
39 | } | ||
40 | |||
41 | if (access(file, F_OK)) { | ||
42 | // no such file | ||
43 | return; | ||
44 | } | ||
45 | |||
46 | |||
47 | char *fname = strdup(file); | ||
48 | if (!fname) | ||
49 | errExit("strdup"); | ||
50 | |||
51 | tf[files_cnt].tfile = fname; | ||
52 | files_cnt++; | ||
53 | } | ||
54 | |||
55 | void sysfiles_test(void) { | ||
56 | // I am root in sandbox mount namespace | ||
57 | assert(user_uid); | ||
58 | int i; | ||
59 | |||
60 | pid_t child = fork(); | ||
61 | if (child == -1) | ||
62 | errExit("fork"); | ||
63 | |||
64 | if (child == 0) { // child | ||
65 | // drop privileges | ||
66 | if (setgid(user_gid) != 0) | ||
67 | errExit("setgid"); | ||
68 | if (setuid(user_uid) != 0) | ||
69 | errExit("setuid"); | ||
70 | |||
71 | for (i = 0; i < files_cnt; i++) { | ||
72 | assert(tf[i].tfile); | ||
73 | |||
74 | // try to open the file for reading | ||
75 | FILE *fp = fopen(tf[i].tfile, "r"); | ||
76 | if (fp) { | ||
77 | |||
78 | printf(" Warning: I can access %s\n", tf[i].tfile); | ||
79 | fclose(fp); | ||
80 | } | ||
81 | } | ||
82 | exit(0); | ||
83 | } | ||
84 | |||
85 | // wait for the child to finish | ||
86 | int status; | ||
87 | wait(&status); | ||
88 | } | ||
diff --git a/src/jailcheck/utils.c b/src/jailcheck/utils.c new file mode 100644 index 000000000..c3aaae298 --- /dev/null +++ b/src/jailcheck/utils.c | |||
@@ -0,0 +1,102 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2021 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 "jailcheck.h" | ||
21 | #include "../include/pid.h" | ||
22 | #include <errno.h> | ||
23 | #include <pwd.h> | ||
24 | #include <dirent.h> | ||
25 | |||
26 | #define BUFLEN 4096 | ||
27 | |||
28 | char *get_sudo_user(void) { | ||
29 | char *user = getenv("SUDO_USER"); | ||
30 | if (!user) { | ||
31 | user = getpwuid(getuid())->pw_name; | ||
32 | if (!user) { | ||
33 | fprintf(stderr, "Error: cannot detect login user\n"); | ||
34 | exit(1); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | return user; | ||
39 | } | ||
40 | |||
41 | char *get_homedir(const char *user, uid_t *uid, gid_t *gid) { | ||
42 | // find home directory | ||
43 | struct passwd *pw = getpwnam(user); | ||
44 | if (!pw) | ||
45 | goto errexit; | ||
46 | |||
47 | char *home = pw->pw_dir; | ||
48 | if (!home) | ||
49 | goto errexit; | ||
50 | |||
51 | *uid = pw->pw_uid; | ||
52 | *gid = pw->pw_gid; | ||
53 | |||
54 | return home; | ||
55 | |||
56 | errexit: | ||
57 | fprintf(stderr, "Error: cannot find home directory for user %s\n", user); | ||
58 | exit(1); | ||
59 | } | ||
60 | |||
61 | // find the second child process for the specified pid | ||
62 | // return -1 if not found | ||
63 | // | ||
64 | // Example: | ||
65 | //14776:netblue:/usr/bin/firejail /usr/bin/transmission-qt | ||
66 | // 14777:netblue:/usr/bin/firejail /usr/bin/transmission-qt | ||
67 | // 14792:netblue:/usr/bin/transmission-qt | ||
68 | // We need 14792, the first real sandboxed process | ||
69 | // duplicate from src/firemon/main.c | ||
70 | int find_child(int id) { | ||
71 | int i; | ||
72 | int first_child = -1; | ||
73 | |||
74 | // find the first child | ||
75 | for (i = 0; i < max_pids; i++) { | ||
76 | if (pids[i].level == 2 && pids[i].parent == id) { | ||
77 | // skip /usr/bin/xdg-dbus-proxy (started by firejail for dbus filtering) | ||
78 | char *cmdline = pid_proc_cmdline(i); | ||
79 | if (strncmp(cmdline, XDG_DBUS_PROXY_PATH, strlen(XDG_DBUS_PROXY_PATH)) == 0) { | ||
80 | free(cmdline); | ||
81 | continue; | ||
82 | } | ||
83 | free(cmdline); | ||
84 | first_child = i; | ||
85 | break; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | if (first_child == -1) | ||
90 | return -1; | ||
91 | |||
92 | // find the second-level child | ||
93 | for (i = 0; i < max_pids; i++) { | ||
94 | if (pids[i].level == 3 && pids[i].parent == first_child) | ||
95 | return i; | ||
96 | } | ||
97 | |||
98 | // if a second child is not found, return the first child pid | ||
99 | // this happens for processes sandboxed with --join | ||
100 | return first_child; | ||
101 | } | ||
102 | |||
diff --git a/src/jailcheck/virtual.c b/src/jailcheck/virtual.c new file mode 100644 index 000000000..09092f9ce --- /dev/null +++ b/src/jailcheck/virtual.c | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2021 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 "jailcheck.h" | ||
21 | #include <dirent.h> | ||
22 | #include <sys/wait.h> | ||
23 | |||
24 | |||
25 | #define MAX_TEST_FILES 16 | ||
26 | static char *dirs[MAX_TEST_FILES]; | ||
27 | static char *files[MAX_TEST_FILES]; | ||
28 | static int files_cnt = 0; | ||
29 | |||
30 | void virtual_setup(const char *directory) { | ||
31 | // I am root! | ||
32 | assert(directory); | ||
33 | assert(*directory == '/'); | ||
34 | assert(files_cnt < MAX_TEST_FILES); | ||
35 | |||
36 | // try to open the dir as root | ||
37 | DIR *dir = opendir(directory); | ||
38 | if (!dir) { | ||
39 | fprintf(stderr, "Warning: directory %s not found, skipping\n", directory); | ||
40 | return; | ||
41 | } | ||
42 | closedir(dir); | ||
43 | |||
44 | // create a test file | ||
45 | char *test_file; | ||
46 | if (asprintf(&test_file, "%s/jailcheck-private-%d", directory, getpid()) == -1) | ||
47 | errExit("asprintf"); | ||
48 | |||
49 | FILE *fp = fopen(test_file, "w"); | ||
50 | if (!fp) { | ||
51 | printf("Warning: I cannot create test file in directory %s, skipping...\n", directory); | ||
52 | return; | ||
53 | } | ||
54 | fprintf(fp, "this file was created by firetest utility, you can safely delete it\n"); | ||
55 | fclose(fp); | ||
56 | if (strcmp(directory, user_home_dir) == 0) { | ||
57 | int rv = chown(test_file, user_uid, user_gid); | ||
58 | if (rv) | ||
59 | errExit("chown"); | ||
60 | } | ||
61 | |||
62 | char *dname = strdup(directory); | ||
63 | if (!dname) | ||
64 | errExit("strdup"); | ||
65 | dirs[files_cnt] = dname; | ||
66 | files[files_cnt] = test_file; | ||
67 | files_cnt++; | ||
68 | } | ||
69 | |||
70 | void virtual_destroy(void) { | ||
71 | // remove test files | ||
72 | int i; | ||
73 | |||
74 | for (i = 0; i < files_cnt; i++) { | ||
75 | int rv = unlink(files[i]); | ||
76 | (void) rv; | ||
77 | } | ||
78 | files_cnt = 0; | ||
79 | } | ||
80 | |||
81 | void virtual_test(void) { | ||
82 | // I am root in sandbox mount namespace | ||
83 | assert(user_uid); | ||
84 | int i; | ||
85 | |||
86 | int cnt = 0; | ||
87 | cnt += printf(" Virtual dirs: "); fflush(0); | ||
88 | |||
89 | for (i = 0; i < files_cnt; i++) { | ||
90 | assert(files[i]); | ||
91 | |||
92 | // I am root! | ||
93 | pid_t child = fork(); | ||
94 | if (child == -1) | ||
95 | errExit("fork"); | ||
96 | |||
97 | if (child == 0) { // child | ||
98 | // drop privileges | ||
99 | if (setgid(user_gid) != 0) | ||
100 | errExit("setgid"); | ||
101 | if (setuid(user_uid) != 0) | ||
102 | errExit("setuid"); | ||
103 | |||
104 | // try to open the file for reading | ||
105 | FILE *fp = fopen(files[i], "r"); | ||
106 | if (fp) | ||
107 | fclose(fp); | ||
108 | else { | ||
109 | if (cnt == 0) | ||
110 | cnt += printf("\n "); | ||
111 | cnt += printf("%s, ", dirs[i]); | ||
112 | if (cnt > 60) | ||
113 | cnt = 0; | ||
114 | } | ||
115 | fflush(0); | ||
116 | exit(cnt); | ||
117 | } | ||
118 | |||
119 | // wait for the child to finish | ||
120 | int status; | ||
121 | wait(&status); | ||
122 | cnt = WEXITSTATUS(status); | ||
123 | } | ||
124 | printf("\n"); | ||
125 | } | ||