diff options
author | netblue30 <netblue30@protonmail.com> | 2021-02-20 10:06:58 -0500 |
---|---|---|
committer | netblue30 <netblue30@protonmail.com> | 2021-02-20 10:06:58 -0500 |
commit | 42e2db1275e37bf669a074c023ea9f9a8b40db43 (patch) | |
tree | 59169acd88cbce9160b1657a7016c789559e0e20 /src | |
parent | run sort.py (diff) | |
download | firejail-42e2db1275e37bf669a074c023ea9f9a8b40db43.tar.gz firejail-42e2db1275e37bf669a074c023ea9f9a8b40db43.tar.zst firejail-42e2db1275e37bf669a074c023ea9f9a8b40db43.zip |
jaitest - simple sandbox testing utility program
Diffstat (limited to 'src')
-rw-r--r-- | src/jailtest/Makefile.in | 14 | ||||
-rw-r--r-- | src/jailtest/access.c | 124 | ||||
-rw-r--r-- | src/jailtest/jailtest.h | 32 | ||||
-rw-r--r-- | src/jailtest/main.c | 134 | ||||
-rw-r--r-- | src/jailtest/noexec.c | 94 | ||||
-rw-r--r-- | src/jailtest/utils.c | 124 | ||||
-rw-r--r-- | src/jailtest/virtual.c | 99 | ||||
-rw-r--r-- | src/man/Makefile.in | 2 | ||||
-rw-r--r-- | src/man/jailtest.txt | 82 |
9 files changed, 704 insertions, 1 deletions
diff --git a/src/jailtest/Makefile.in b/src/jailtest/Makefile.in new file mode 100644 index 000000000..9c9c0c508 --- /dev/null +++ b/src/jailtest/Makefile.in | |||
@@ -0,0 +1,14 @@ | |||
1 | all: jailtest | ||
2 | |||
3 | include ../common.mk | ||
4 | |||
5 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/pid.h | ||
6 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ | ||
7 | |||
8 | jailtest: $(OBJS) | ||
9 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/pid.o $(LIBS) $(EXTRA_LDFLAGS) | ||
10 | |||
11 | clean:; rm -fr *.o jailtest *.gcov *.gcda *.gcno *.plist | ||
12 | |||
13 | distclean: clean | ||
14 | rm -fr Makefile | ||
diff --git a/src/jailtest/access.c b/src/jailtest/access.c new file mode 100644 index 000000000..e68227bd2 --- /dev/null +++ b/src/jailtest/access.c | |||
@@ -0,0 +1,124 @@ | |||
1 | #include "jailtest.h" | ||
2 | #include <dirent.h> | ||
3 | #include <sys/wait.h> | ||
4 | |||
5 | typedef struct { | ||
6 | char *tfile; | ||
7 | char *tdir; | ||
8 | } TestDir; | ||
9 | |||
10 | #define MAX_TEST_FILES 16 | ||
11 | TestDir td[MAX_TEST_FILES]; | ||
12 | static int files_cnt = 0; | ||
13 | |||
14 | void access_setup(const char *directory) { | ||
15 | // I am root! | ||
16 | assert(directory); | ||
17 | assert(user_home_dir); | ||
18 | |||
19 | if (files_cnt >= MAX_TEST_FILES) { | ||
20 | fprintf(stderr, "Error: maximum number of test directories exceded\n"); | ||
21 | exit(1); | ||
22 | } | ||
23 | |||
24 | char *fname = strdup(directory); | ||
25 | if (!fname) | ||
26 | errExit("strdup"); | ||
27 | if (strncmp(fname, "~/", 2) == 0) { | ||
28 | free(fname); | ||
29 | if (asprintf(&fname, "%s/%s", user_home_dir, directory + 2) == -1) | ||
30 | errExit("asprintf"); | ||
31 | } | ||
32 | |||
33 | char *path = realpath(fname, NULL); | ||
34 | free(fname); | ||
35 | if (path == NULL) { | ||
36 | fprintf(stderr, "Warning: invalid directory %s, skipping...\n", directory); | ||
37 | return; | ||
38 | } | ||
39 | |||
40 | // file in home directory | ||
41 | if (strncmp(path, user_home_dir, strlen(user_home_dir)) != 0) { | ||
42 | fprintf(stderr, "Warning: file %s is not in user home directory, skipping...\n", directory); | ||
43 | free(path); | ||
44 | return; | ||
45 | } | ||
46 | |||
47 | // try to open the dir as root | ||
48 | DIR *dir = opendir(path); | ||
49 | if (!dir) { | ||
50 | fprintf(stderr, "Warning: directory %s not found, skipping\n", directory); | ||
51 | free(path); | ||
52 | return; | ||
53 | } | ||
54 | closedir(dir); | ||
55 | |||
56 | // create a test file | ||
57 | char *test_file; | ||
58 | if (asprintf(&test_file, "%s/jailtest-access-%d", path, getpid()) == -1) | ||
59 | errExit("asprintf"); | ||
60 | |||
61 | FILE *fp = fopen(test_file, "w"); | ||
62 | if (!fp) { | ||
63 | printf("Warning: I cannot create test file in directory %s, skipping...\n", directory); | ||
64 | return; | ||
65 | } | ||
66 | fprintf(fp, "this file was created by firetest utility, you can safely delete it\n"); | ||
67 | fclose(fp); | ||
68 | int rv = chown(test_file, user_uid, user_gid); | ||
69 | if (rv) | ||
70 | errExit("chown"); | ||
71 | |||
72 | char *dname = strdup(directory); | ||
73 | if (!dname) | ||
74 | errExit("strdup"); | ||
75 | td[files_cnt].tdir = dname; | ||
76 | td[files_cnt].tfile = test_file; | ||
77 | files_cnt++; | ||
78 | } | ||
79 | |||
80 | void access_destroy(void) { | ||
81 | // remove test files | ||
82 | int i; | ||
83 | |||
84 | for (i = 0; i < files_cnt; i++) { | ||
85 | int rv = unlink(td[i].tfile); | ||
86 | (void) rv; | ||
87 | } | ||
88 | files_cnt = 0; | ||
89 | } | ||
90 | |||
91 | void access_test(void) { | ||
92 | // I am root in sandbox mount namespace | ||
93 | assert(user_uid); | ||
94 | int i; | ||
95 | |||
96 | pid_t child = fork(); | ||
97 | if (child == -1) | ||
98 | errExit("fork"); | ||
99 | |||
100 | if (child == 0) { // child | ||
101 | // drop privileges | ||
102 | if (setgid(user_gid) != 0) | ||
103 | errExit("setgid"); | ||
104 | if (setuid(user_uid) != 0) | ||
105 | errExit("setuid"); | ||
106 | |||
107 | for (i = 0; i < files_cnt; i++) { | ||
108 | assert(td[i].tfile); | ||
109 | |||
110 | // try to open the file for reading | ||
111 | FILE *fp = fopen(td[i].tfile, "r"); | ||
112 | if (fp) { | ||
113 | |||
114 | printf(" Warning: I can read %s\n", td[i].tdir); | ||
115 | fclose(fp); | ||
116 | } | ||
117 | } | ||
118 | exit(0); | ||
119 | } | ||
120 | |||
121 | // wait for the child to finish | ||
122 | int status; | ||
123 | wait(&status); | ||
124 | } | ||
diff --git a/src/jailtest/jailtest.h b/src/jailtest/jailtest.h new file mode 100644 index 000000000..678f94bef --- /dev/null +++ b/src/jailtest/jailtest.h | |||
@@ -0,0 +1,32 @@ | |||
1 | #ifndef JAILTEST_H | ||
2 | #define JAILTEST_H | ||
3 | |||
4 | #include "../include/common.h" | ||
5 | |||
6 | // main.c | ||
7 | extern uid_t user_uid; | ||
8 | extern gid_t user_gid; | ||
9 | extern char *user_name; | ||
10 | extern char *user_home_dir; | ||
11 | |||
12 | // access.c | ||
13 | void access_setup(const char *directory); | ||
14 | void access_test(void); | ||
15 | void access_destroy(void); | ||
16 | |||
17 | // noexec.c | ||
18 | void noexec_setup(void); | ||
19 | void noexec_test(const char *msg); | ||
20 | |||
21 | // virtual.c | ||
22 | void virtual_setup(const char *directory); | ||
23 | void virtual_destroy(void); | ||
24 | void virtual_test(void); | ||
25 | |||
26 | // utils.c | ||
27 | char *get_sudo_user(void); | ||
28 | char *get_homedir(const char *user, uid_t *uid, gid_t *gid); | ||
29 | int find_child(pid_t parent, pid_t *child); | ||
30 | pid_t switch_to_child(pid_t pid); | ||
31 | |||
32 | #endif \ No newline at end of file | ||
diff --git a/src/jailtest/main.c b/src/jailtest/main.c new file mode 100644 index 000000000..78f162706 --- /dev/null +++ b/src/jailtest/main.c | |||
@@ -0,0 +1,134 @@ | |||
1 | #include "jailtest.h" | ||
2 | #include "../include/firejail_user.h" | ||
3 | #include "../include/pid.h" | ||
4 | #include <sys/wait.h> | ||
5 | |||
6 | uid_t user_uid = 0; | ||
7 | gid_t user_gid = 0; | ||
8 | char *user_name = NULL; | ||
9 | char *user_home_dir = NULL; | ||
10 | int arg_debug = 0; | ||
11 | |||
12 | static char *usage_str = | ||
13 | "Usage: jailtest [options] directory [directory]\n\n" | ||
14 | "Options:\n" | ||
15 | " --debug - print debug messages.\n" | ||
16 | " --help, -? - this help screen.\n" | ||
17 | " --version - print program version and exit.\n"; | ||
18 | |||
19 | |||
20 | static void usage(void) { | ||
21 | printf("firetest - version %s\n\n", VERSION); | ||
22 | puts(usage_str); | ||
23 | } | ||
24 | |||
25 | static void cleanup(void) { | ||
26 | // running only as root | ||
27 | if (getuid() == 0) { | ||
28 | if (arg_debug) | ||
29 | printf("cleaning up!\n"); | ||
30 | access_destroy(); | ||
31 | virtual_destroy(); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | int main(int argc, char **argv) { | ||
36 | int i; | ||
37 | int findex = 0; | ||
38 | |||
39 | for (i = 1; i < argc; i++) { | ||
40 | if (strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "--help") == 0) { | ||
41 | usage(); | ||
42 | return 0; | ||
43 | } | ||
44 | else if (strcmp(argv[i], "--version") == 0) { | ||
45 | printf("firetest version %s\n\n", VERSION); | ||
46 | return 0; | ||
47 | } | ||
48 | else if (strncmp(argv[i], "--hello=", 8) == 0) { // used by noexec test | ||
49 | printf(" Warning: I can run programs in %s\n", argv[i] + 8); | ||
50 | return 0; | ||
51 | } | ||
52 | else if (strcmp(argv[i], "--debug") == 0) | ||
53 | arg_debug = 1; | ||
54 | else if (strncmp(argv[i], "--", 2) == 0) { | ||
55 | fprintf(stderr, "Error: invalid option\n"); | ||
56 | return 1; | ||
57 | } | ||
58 | else { | ||
59 | findex = i; | ||
60 | break; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | // user setup | ||
65 | if (getuid() != 0) { | ||
66 | fprintf(stderr, "Error: you need to be root (via sudo) to run this program\n"); | ||
67 | exit(1); | ||
68 | } | ||
69 | user_name = get_sudo_user(); | ||
70 | assert(user_name); | ||
71 | user_home_dir = get_homedir(user_name, &user_uid, &user_gid); | ||
72 | if (user_uid == 0) { | ||
73 | fprintf(stderr, "Error: root user not supported\n"); | ||
74 | exit(1); | ||
75 | } | ||
76 | |||
77 | // test setup | ||
78 | atexit(cleanup); | ||
79 | if (findex > 0) { | ||
80 | for (i = findex; i < argc; i++) | ||
81 | access_setup(argv[i]); | ||
82 | } | ||
83 | |||
84 | noexec_setup(); | ||
85 | virtual_setup(user_home_dir); | ||
86 | virtual_setup("/tmp"); | ||
87 | virtual_setup("/var/tmp"); | ||
88 | virtual_setup("/dev"); | ||
89 | virtual_setup("/etc"); | ||
90 | virtual_setup("/bin"); | ||
91 | |||
92 | // print processes | ||
93 | pid_read(0); | ||
94 | for (i = 0; i < max_pids; i++) { | ||
95 | if (pids[i].level == 1) { | ||
96 | uid_t uid = pid_get_uid(i); | ||
97 | if (uid != user_uid) // not interested in other user sandboxes | ||
98 | continue; | ||
99 | |||
100 | // in case the pid is that of a firejail process, use the pid of the first child process | ||
101 | uid_t pid = switch_to_child(i); | ||
102 | pid_print_list(i, 0); // no wrapping | ||
103 | |||
104 | pid_t child = fork(); | ||
105 | if (child == -1) | ||
106 | errExit("fork"); | ||
107 | if (child == 0) { | ||
108 | int rv = join_namespace(pid, "mnt"); | ||
109 | if (rv == 0) { | ||
110 | virtual_test(); | ||
111 | noexec_test(user_home_dir); | ||
112 | noexec_test("/tmp"); | ||
113 | noexec_test("/var/tmp"); | ||
114 | access_test(); | ||
115 | } | ||
116 | else { | ||
117 | printf(" Error: I cannot join the process mount space\n"); | ||
118 | exit(1); | ||
119 | } | ||
120 | |||
121 | // drop privileges in order not to trigger cleanup() | ||
122 | if (setgid(user_gid) != 0) | ||
123 | errExit("setgid"); | ||
124 | if (setuid(user_uid) != 0) | ||
125 | errExit("setuid"); | ||
126 | return 0; | ||
127 | } | ||
128 | int status; | ||
129 | wait(&status); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | return 0; | ||
134 | } | ||
diff --git a/src/jailtest/noexec.c b/src/jailtest/noexec.c new file mode 100644 index 000000000..d2f85514a --- /dev/null +++ b/src/jailtest/noexec.c | |||
@@ -0,0 +1,94 @@ | |||
1 | #include "jailtest.h" | ||
2 | #include <sys/wait.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include <fcntl.h> | ||
5 | |||
6 | static unsigned char *execfile = NULL; | ||
7 | static int execfile_len = 0; | ||
8 | |||
9 | void noexec_setup(void) { | ||
10 | // grab a copy of myself | ||
11 | char *self = realpath("/proc/self/exe", NULL); | ||
12 | if (self) { | ||
13 | struct stat s; | ||
14 | if (access(self, X_OK) == 0 && stat(self, &s) == 0) { | ||
15 | assert(s.st_size); | ||
16 | execfile = malloc(s.st_size); | ||
17 | |||
18 | int fd = open(self, O_RDONLY); | ||
19 | if (fd == -1) | ||
20 | errExit("open"); | ||
21 | int len = 0; | ||
22 | do { | ||
23 | int rv = read(fd, execfile + len, s.st_size - len); | ||
24 | if (rv == -1) | ||
25 | errExit("read"); | ||
26 | if (rv == 0) { | ||
27 | // something went wrong! | ||
28 | free(execfile); | ||
29 | execfile = NULL; | ||
30 | printf("Warning: I cannot grab a copy of myself, skipping noexec test...\n"); | ||
31 | break; | ||
32 | } | ||
33 | len += rv; | ||
34 | } | ||
35 | while (len < s.st_size); | ||
36 | execfile_len = s.st_size; | ||
37 | close(fd); | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | |||
42 | |||
43 | void noexec_test(const char *path) { | ||
44 | assert(user_uid); | ||
45 | |||
46 | // I am root in sandbox mount namespace | ||
47 | if (!execfile) | ||
48 | return; | ||
49 | |||
50 | char *fname; | ||
51 | if (asprintf(&fname, "%s/jailtest-noexec-%d", path, getpid()) == -1) | ||
52 | errExit("asprintf"); | ||
53 | |||
54 | pid_t child = fork(); | ||
55 | if (child == -1) | ||
56 | errExit("fork"); | ||
57 | |||
58 | if (child == 0) { // child | ||
59 | // drop privileges | ||
60 | if (setgid(user_gid) != 0) | ||
61 | errExit("setgid"); | ||
62 | if (setuid(user_uid) != 0) | ||
63 | errExit("setuid"); | ||
64 | int fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0700); | ||
65 | if (fd == -1) { | ||
66 | printf(" I cannot create files in %s, skipping noexec...\n", path); | ||
67 | exit(1); | ||
68 | } | ||
69 | |||
70 | int len = 0; | ||
71 | while (len < execfile_len) { | ||
72 | int rv = write(fd, execfile + len, execfile_len - len); | ||
73 | if (rv == -1 || rv == 0) { | ||
74 | printf(" I cannot create files in %s, skipping noexec....\n", path); | ||
75 | exit(1); | ||
76 | } | ||
77 | len += rv; | ||
78 | } | ||
79 | fchmod(fd, 0700); | ||
80 | close(fd); | ||
81 | |||
82 | char *arg; | ||
83 | if (asprintf(&arg, "--hello=%s", path) == -1) | ||
84 | errExit("asprintf"); | ||
85 | int rv = execl(fname, fname, arg, NULL); | ||
86 | (void) rv; // if we get here execl failed | ||
87 | exit(0); | ||
88 | } | ||
89 | |||
90 | int status; | ||
91 | wait(&status); | ||
92 | int rv = unlink(fname); | ||
93 | (void) rv; | ||
94 | } \ No newline at end of file | ||
diff --git a/src/jailtest/utils.c b/src/jailtest/utils.c new file mode 100644 index 000000000..b24783355 --- /dev/null +++ b/src/jailtest/utils.c | |||
@@ -0,0 +1,124 @@ | |||
1 | #include "jailtest.h" | ||
2 | #include <errno.h> | ||
3 | #include <pwd.h> | ||
4 | #include <dirent.h> | ||
5 | |||
6 | #define BUFLEN 4096 | ||
7 | |||
8 | char *get_sudo_user(void) { | ||
9 | char *user = getenv("SUDO_USER"); | ||
10 | if (!user) { | ||
11 | user = getpwuid(getuid())->pw_name; | ||
12 | if (!user) { | ||
13 | fprintf(stderr, "Error: cannot detect login user\n"); | ||
14 | exit(1); | ||
15 | } | ||
16 | } | ||
17 | |||
18 | return user; | ||
19 | } | ||
20 | |||
21 | char *get_homedir(const char *user, uid_t *uid, gid_t *gid) { | ||
22 | // find home directory | ||
23 | struct passwd *pw = getpwnam(user); | ||
24 | if (!pw) | ||
25 | goto errexit; | ||
26 | |||
27 | char *home = pw->pw_dir; | ||
28 | if (!home) | ||
29 | goto errexit; | ||
30 | |||
31 | *uid = pw->pw_uid; | ||
32 | *gid = pw->pw_gid; | ||
33 | |||
34 | return home; | ||
35 | |||
36 | errexit: | ||
37 | fprintf(stderr, "Error: cannot find home directory for user %s\n", user); | ||
38 | exit(1); | ||
39 | } | ||
40 | |||
41 | int find_child(pid_t parent, pid_t *child) { | ||
42 | *child = 0; // use it to flag a found child | ||
43 | |||
44 | DIR *dir; | ||
45 | if (!(dir = opendir("/proc"))) { | ||
46 | // sleep 2 seconds and try again | ||
47 | sleep(2); | ||
48 | if (!(dir = opendir("/proc"))) { | ||
49 | fprintf(stderr, "Error: cannot open /proc directory\n"); | ||
50 | exit(1); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | struct dirent *entry; | ||
55 | char *end; | ||
56 | while (*child == 0 && (entry = readdir(dir))) { | ||
57 | pid_t pid = strtol(entry->d_name, &end, 10); | ||
58 | if (end == entry->d_name || *end) | ||
59 | continue; | ||
60 | if (pid == parent) | ||
61 | continue; | ||
62 | |||
63 | // open stat file | ||
64 | char *file; | ||
65 | if (asprintf(&file, "/proc/%u/status", pid) == -1) { | ||
66 | perror("asprintf"); | ||
67 | exit(1); | ||
68 | } | ||
69 | FILE *fp = fopen(file, "r"); | ||
70 | if (!fp) { | ||
71 | free(file); | ||
72 | continue; | ||
73 | } | ||
74 | |||
75 | // look for firejail executable name | ||
76 | char buf[BUFLEN]; | ||
77 | while (fgets(buf, BUFLEN - 1, fp)) { | ||
78 | if (strncmp(buf, "PPid:", 5) == 0) { | ||
79 | char *ptr = buf + 5; | ||
80 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { | ||
81 | ptr++; | ||
82 | } | ||
83 | if (*ptr == '\0') { | ||
84 | fprintf(stderr, "Error: cannot read /proc file\n"); | ||
85 | exit(1); | ||
86 | } | ||
87 | if (parent == atoi(ptr)) { | ||
88 | // we don't want /usr/bin/xdg-dbus-proxy! | ||
89 | char *cmdline = pid_proc_cmdline(pid); | ||
90 | if (strncmp(cmdline, XDG_DBUS_PROXY_PATH, strlen(XDG_DBUS_PROXY_PATH)) != 0) | ||
91 | *child = pid; | ||
92 | free(cmdline); | ||
93 | } | ||
94 | break; // stop reading the file | ||
95 | } | ||
96 | } | ||
97 | fclose(fp); | ||
98 | free(file); | ||
99 | } | ||
100 | closedir(dir); | ||
101 | return (*child)? 0:1; // 0 = found, 1 = not found | ||
102 | } | ||
103 | |||
104 | pid_t switch_to_child(pid_t pid) { | ||
105 | pid_t rv = pid; | ||
106 | errno = 0; | ||
107 | char *comm = pid_proc_comm(pid); | ||
108 | if (!comm) { | ||
109 | if (errno == ENOENT) | ||
110 | fprintf(stderr, "Error: cannot find process with pid %d\n", pid); | ||
111 | else | ||
112 | fprintf(stderr, "Error: cannot read /proc file\n"); | ||
113 | exit(1); | ||
114 | } | ||
115 | |||
116 | if (strcmp(comm, "firejail") == 0) { | ||
117 | if (find_child(pid, &rv) == 1) { | ||
118 | fprintf(stderr, "Error: no valid sandbox\n"); | ||
119 | exit(1); | ||
120 | } | ||
121 | } | ||
122 | free(comm); | ||
123 | return rv; | ||
124 | } | ||
diff --git a/src/jailtest/virtual.c b/src/jailtest/virtual.c new file mode 100644 index 000000000..48296fdb1 --- /dev/null +++ b/src/jailtest/virtual.c | |||
@@ -0,0 +1,99 @@ | |||
1 | #include "jailtest.h" | ||
2 | #include <dirent.h> | ||
3 | #include <sys/wait.h> | ||
4 | |||
5 | |||
6 | #define MAX_TEST_FILES 16 | ||
7 | static char *dirs[MAX_TEST_FILES]; | ||
8 | static char *files[MAX_TEST_FILES]; | ||
9 | static int files_cnt = 0; | ||
10 | |||
11 | void virtual_setup(const char *directory) { | ||
12 | // I am root! | ||
13 | assert(directory); | ||
14 | assert(*directory == '/'); | ||
15 | assert(files_cnt < MAX_TEST_FILES); | ||
16 | |||
17 | // try to open the dir as root | ||
18 | DIR *dir = opendir(directory); | ||
19 | if (!dir) { | ||
20 | fprintf(stderr, "Warning: directory %s not found, skipping\n", directory); | ||
21 | return; | ||
22 | } | ||
23 | closedir(dir); | ||
24 | |||
25 | // create a test file | ||
26 | char *test_file; | ||
27 | if (asprintf(&test_file, "%s/jailtest-private-%d", directory, getpid()) == -1) | ||
28 | errExit("asprintf"); | ||
29 | |||
30 | FILE *fp = fopen(test_file, "w"); | ||
31 | if (!fp) { | ||
32 | printf("Warning: I cannot create test file in directory %s, skipping...\n", directory); | ||
33 | return; | ||
34 | } | ||
35 | fprintf(fp, "this file was created by firetest utility, you can safely delete it\n"); | ||
36 | fclose(fp); | ||
37 | if (strcmp(directory, user_home_dir) == 0) { | ||
38 | int rv = chown(test_file, user_uid, user_gid); | ||
39 | if (rv) | ||
40 | errExit("chown"); | ||
41 | } | ||
42 | |||
43 | char *dname = strdup(directory); | ||
44 | if (!dname) | ||
45 | errExit("strdup"); | ||
46 | dirs[files_cnt] = dname; | ||
47 | files[files_cnt] = test_file; | ||
48 | files_cnt++; | ||
49 | } | ||
50 | |||
51 | void virtual_destroy(void) { | ||
52 | // remove test files | ||
53 | int i; | ||
54 | |||
55 | for (i = 0; i < files_cnt; i++) { | ||
56 | int rv = unlink(files[i]); | ||
57 | (void) rv; | ||
58 | } | ||
59 | files_cnt = 0; | ||
60 | } | ||
61 | |||
62 | void virtual_test(void) { | ||
63 | // I am root in sandbox mount namespace | ||
64 | assert(user_uid); | ||
65 | int i; | ||
66 | |||
67 | printf(" Virtual dirs: "); fflush(0); | ||
68 | |||
69 | for (i = 0; i < files_cnt; i++) { | ||
70 | assert(files[i]); | ||
71 | |||
72 | // I am root! | ||
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 | |||
84 | // try to open the file for reading | ||
85 | FILE *fp = fopen(files[i], "r"); | ||
86 | if (fp) | ||
87 | fclose(fp); | ||
88 | else | ||
89 | printf("%s, ", dirs[i]); | ||
90 | fflush(0); | ||
91 | exit(0); | ||
92 | } | ||
93 | |||
94 | // wait for the child to finish | ||
95 | int status; | ||
96 | wait(&status); | ||
97 | } | ||
98 | printf("\n"); | ||
99 | } | ||
diff --git a/src/man/Makefile.in b/src/man/Makefile.in index 1c4444307..1a1f8ba08 100644 --- a/src/man/Makefile.in +++ b/src/man/Makefile.in | |||
@@ -1,4 +1,4 @@ | |||
1 | all: firecfg.man firejail.man firejail-login.man firejail-users.man firejail-profile.man firemon.man | 1 | all: firecfg.man firejail.man firejail-login.man firejail-users.man firejail-profile.man firemon.man jailtest.man |
2 | include ../common.mk | 2 | include ../common.mk |
3 | 3 | ||
4 | %.man: %.txt | 4 | %.man: %.txt |
diff --git a/src/man/jailtest.txt b/src/man/jailtest.txt new file mode 100644 index 000000000..bc1999163 --- /dev/null +++ b/src/man/jailtest.txt | |||
@@ -0,0 +1,82 @@ | |||
1 | .TH JAILTEST 1 "MONTH YEAR" "VERSION" "JAILTEST man page" | ||
2 | .SH NAME | ||
3 | jailtest \- Simple utility program to test running sandboxes | ||
4 | .SH SYNOPSIS | ||
5 | sudo jailtest [OPTIONS] [directory] | ||
6 | .SH DESCRIPTION | ||
7 | WORK IN PROGRESS! | ||
8 | jailtest attaches itself to all sandboxes started by the user and performs some basic tests | ||
9 | on the sandbox filesystem: | ||
10 | .TP | ||
11 | \fB1. Virtual directories | ||
12 | jailtest extracts a list with the main virtual directories installed by the sandbox. | ||
13 | These directories are build by firejail at startup using --private* and --whitelist commands. | ||
14 | .TP | ||
15 | \fB2. Noexec test | ||
16 | jailtest inserts executable programs in /home/username, /tmp, and /var/tmp directories | ||
17 | and tries to run them form inside the sandbox, thus testing if the directory is executable or not. | ||
18 | .TP | ||
19 | \fB3. Read access test | ||
20 | jailtest creates test files in the directories specified by the user and tries to read | ||
21 | them from inside the sandbox. | ||
22 | |||
23 | .TP | ||
24 | The program is running as root exclusively under sudo. | ||
25 | |||
26 | .SH OPTIONS | ||
27 | .TP | ||
28 | \fB\-\-debug | ||
29 | Print debug messages | ||
30 | .TP | ||
31 | \fB\-?\fR, \fB\-\-help\fR | ||
32 | Print options end exit. | ||
33 | .TP | ||
34 | \fB\-\-version | ||
35 | Print program version and exit. | ||
36 | .TP | ||
37 | \fB[directory] | ||
38 | One or more directories in user home to test for read access. | ||
39 | |||
40 | .SH OUTPUT | ||
41 | For each sandbox detected we print the following line: | ||
42 | |||
43 | PID:USER:Sandbox Name:Command | ||
44 | |||
45 | It is followed by relevant sandbox information, such as the virtual directories and various warnings. | ||
46 | |||
47 | .SH EXAMPLE | ||
48 | |||
49 | .br | ||
50 | $ sudo jailtest ~/.ssh ~/.gnupg | ||
51 | .br | ||
52 | 1429:netblue::/usr/bin/firejail /opt/firefox/firefox | ||
53 | .br | ||
54 | Virtual dirs: /home/netblue, /tmp, /var/tmp, /dev, /etc, | ||
55 | .br | ||
56 | 5602:netblue::/usr/bin/firejail /usr/bin/ssh netblue@x.y.z.net | ||
57 | .br | ||
58 | Virtual dirs: /var/tmp, /dev, | ||
59 | .br | ||
60 | Warning: I can read ~/.ssh | ||
61 | .br | ||
62 | 5926:netblue::/usr/bin/firejail /usr/bin/gimp-2.10 | ||
63 | .br | ||
64 | Virtual dirs: /tmp, /var/tmp, /dev, | ||
65 | .br | ||
66 | Warning: I can run programs in /home/netblue | ||
67 | .br | ||
68 | 6394:netblue:libreoffice:/usr/bin/firejail libreoffice | ||
69 | .br | ||
70 | Virtual dirs: /tmp, /var/tmp, /dev, | ||
71 | .br | ||
72 | |||
73 | .SH LICENSE | ||
74 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. | ||
75 | .PP | ||
76 | Homepage: https://firejail.wordpress.com | ||
77 | .SH SEE ALSO | ||
78 | \&\flfirejail\fR\|(1), | ||
79 | \&\flfirecfg\fR\|(1), | ||
80 | \&\flfirejail-profile\fR\|(5), | ||
81 | \&\flfirejail-login\fR\|(5) | ||
82 | \&\flfirejail-users\fR\|(5) | ||