aboutsummaryrefslogtreecommitdiffstats
path: root/src/jailtest
diff options
context:
space:
mode:
Diffstat (limited to 'src/jailtest')
-rw-r--r--src/jailtest/Makefile.in14
-rw-r--r--src/jailtest/access.c124
-rw-r--r--src/jailtest/jailtest.h32
-rw-r--r--src/jailtest/main.c134
-rw-r--r--src/jailtest/noexec.c94
-rw-r--r--src/jailtest/utils.c124
-rw-r--r--src/jailtest/virtual.c99
7 files changed, 621 insertions, 0 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 @@
1all: jailtest
2
3include ../common.mk
4
5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/pid.h
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7
8jailtest: $(OBJS)
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/pid.o $(LIBS) $(EXTRA_LDFLAGS)
10
11clean:; rm -fr *.o jailtest *.gcov *.gcda *.gcno *.plist
12
13distclean: 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
5typedef struct {
6 char *tfile;
7 char *tdir;
8} TestDir;
9
10#define MAX_TEST_FILES 16
11TestDir td[MAX_TEST_FILES];
12static int files_cnt = 0;
13
14void 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
80void 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
91void 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
7extern uid_t user_uid;
8extern gid_t user_gid;
9extern char *user_name;
10extern char *user_home_dir;
11
12// access.c
13void access_setup(const char *directory);
14void access_test(void);
15void access_destroy(void);
16
17// noexec.c
18void noexec_setup(void);
19void noexec_test(const char *msg);
20
21// virtual.c
22void virtual_setup(const char *directory);
23void virtual_destroy(void);
24void virtual_test(void);
25
26// utils.c
27char *get_sudo_user(void);
28char *get_homedir(const char *user, uid_t *uid, gid_t *gid);
29int find_child(pid_t parent, pid_t *child);
30pid_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
6uid_t user_uid = 0;
7gid_t user_gid = 0;
8char *user_name = NULL;
9char *user_home_dir = NULL;
10int arg_debug = 0;
11
12static 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
20static void usage(void) {
21 printf("firetest - version %s\n\n", VERSION);
22 puts(usage_str);
23}
24
25static 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
35int 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
6static unsigned char *execfile = NULL;
7static int execfile_len = 0;
8
9void 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
43void 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
8char *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
21char *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
36errexit:
37 fprintf(stderr, "Error: cannot find home directory for user %s\n", user);
38 exit(1);
39}
40
41int 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
104pid_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
7static char *dirs[MAX_TEST_FILES];
8static char *files[MAX_TEST_FILES];
9static int files_cnt = 0;
10
11void 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
51void 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
62void 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}