aboutsummaryrefslogtreecommitdiffstats
path: root/src/jailcheck
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@protonmail.com>2021-05-18 13:49:02 -0400
committerLibravatar netblue30 <netblue30@protonmail.com>2021-05-18 13:49:02 -0400
commitb79e4416fe642976111a2d610a19c3e4696bb2e2 (patch)
treec038806bb80d57314a248dbc6df92b91d32a3a59 /src/jailcheck
parentreadme, etc (diff)
downloadfirejail-b79e4416fe642976111a2d610a19c3e4696bb2e2.tar.gz
firejail-b79e4416fe642976111a2d610a19c3e4696bb2e2.tar.zst
firejail-b79e4416fe642976111a2d610a19c3e4696bb2e2.zip
jailtest -> jailcheck (#4268)
Diffstat (limited to 'src/jailcheck')
-rw-r--r--src/jailcheck/Makefile.in17
-rw-r--r--src/jailcheck/access.c143
-rw-r--r--src/jailcheck/apparmor.c40
-rw-r--r--src/jailcheck/jailcheck.h62
-rw-r--r--src/jailcheck/main.c192
-rw-r--r--src/jailcheck/noexec.c113
-rw-r--r--src/jailcheck/seccomp.c47
-rw-r--r--src/jailcheck/sysfiles.c88
-rw-r--r--src/jailcheck/utils.c102
-rw-r--r--src/jailcheck/virtual.c125
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
2all: jailcheck
3
4include ../common.mk
5
6%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/pid.h
7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
8
9jailcheck: $(OBJS)
10 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/pid.o $(LIBS) $(EXTRA_LDFLAGS)
11
12.PHONY: clean
13clean:; rm -fr *.o jailcheck *.gcov *.gcda *.gcno *.plist
14
15.PHONY: distclean
16distclean: 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
24typedef struct {
25 char *tfile;
26 char *tdir;
27} TestDir;
28
29#define MAX_TEST_FILES 16
30TestDir td[MAX_TEST_FILES];
31static int files_cnt = 0;
32
33void 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
99void 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
110void 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
25void 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
35void 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
26extern uid_t user_uid;
27extern gid_t user_gid;
28extern char *user_name;
29extern char *user_home_dir;
30extern char *user_run_dir;
31
32// access.c
33void access_setup(const char *directory);
34void access_test(void);
35void access_destroy(void);
36
37// noexec.c
38void noexec_setup(void);
39void noexec_test(const char *msg);
40
41// sysfiles.c
42void sysfiles_setup(const char *file);
43void sysfiles_test(void);
44
45// virtual.c
46void virtual_setup(const char *directory);
47void virtual_destroy(void);
48void virtual_test(void);
49
50// apparmor.c
51void apparmor_test(pid_t pid);
52
53// seccomp.c
54void seccomp_test(pid_t pid);
55
56// utils.c
57char *get_sudo_user(void);
58char *get_homedir(const char *user, uid_t *uid, gid_t *gid);
59int find_child(pid_t pid);
60pid_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
25uid_t user_uid = 0;
26gid_t user_gid = 0;
27char *user_name = NULL;
28char *user_home_dir = NULL;
29char *user_run_dir = NULL;
30int arg_debug = 0;
31
32static 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
40static void usage(void) {
41 printf("firetest - version %s\n\n", VERSION);
42 puts(usage_str);
43}
44
45static 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
55int 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
25static unsigned char *execfile = NULL;
26static int execfile_len = 0;
27
28void 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
62void 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
23void 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
24typedef struct {
25 char *tfile;
26} TestFile;
27
28#define MAX_TEST_FILES 32
29TestFile tf[MAX_TEST_FILES];
30static int files_cnt = 0;
31
32void 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
55void 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
28char *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
41char *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
56errexit:
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
70int 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
26static char *dirs[MAX_TEST_FILES];
27static char *files[MAX_TEST_FILES];
28static int files_cnt = 0;
29
30void 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
70void 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
81void 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}