diff options
Diffstat (limited to 'src/firejail/fs_whitelist.c')
-rw-r--r-- | src/firejail/fs_whitelist.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c new file mode 100644 index 000000000..fac08705d --- /dev/null +++ b/src/firejail/fs_whitelist.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014, 2015 Firejail Authors | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | #include "firejail.h" | ||
21 | #include <sys/mount.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <linux/limits.h> | ||
24 | #include <fnmatch.h> | ||
25 | #include <glob.h> | ||
26 | #include <dirent.h> | ||
27 | #include <fcntl.h> | ||
28 | #include <errno.h> | ||
29 | |||
30 | static int mkpath(const char* path, mode_t mode) { | ||
31 | assert(path && *path); | ||
32 | |||
33 | // work on a copy of the path | ||
34 | char *file_path = strdup(path); | ||
35 | if (!file_path) | ||
36 | errExit("strdup"); | ||
37 | |||
38 | char* p; | ||
39 | for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) { | ||
40 | *p='\0'; | ||
41 | if (mkdir(file_path, mode)==-1) { | ||
42 | if (errno != EEXIST) { | ||
43 | *p='/'; | ||
44 | free(file_path); | ||
45 | return -1; | ||
46 | } | ||
47 | } | ||
48 | else { | ||
49 | // TODO: set correct directory mode and properties | ||
50 | } | ||
51 | |||
52 | *p='/'; | ||
53 | } | ||
54 | |||
55 | free(file_path); | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | static void whitelist_path(const char *path) { | ||
60 | assert(path); | ||
61 | |||
62 | // fname needs to start with /home/username | ||
63 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir))) { | ||
64 | fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path); | ||
65 | exit(1); | ||
66 | } | ||
67 | |||
68 | const char *fname = path + strlen(cfg.homedir); | ||
69 | if (*fname == '\0') { | ||
70 | fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path); | ||
71 | exit(1); | ||
72 | } | ||
73 | |||
74 | char *wfile; | ||
75 | if (asprintf(&wfile, "%s/%s", WHITELIST_HOME_DIR, fname) == -1) | ||
76 | errExit("asprintf"); | ||
77 | |||
78 | // check if the file exists | ||
79 | struct stat s; | ||
80 | if (stat(wfile, &s) == 0) { | ||
81 | if (arg_debug) | ||
82 | printf("Whitelisting %s\n", path); | ||
83 | } | ||
84 | else { | ||
85 | if (arg_debug) { | ||
86 | fprintf(stderr, "Warning: %s is an invalid file, skipping...\n", path); | ||
87 | } | ||
88 | return; | ||
89 | } | ||
90 | |||
91 | // create the path if necessary | ||
92 | mkpath(path, 0755); | ||
93 | |||
94 | // process directory | ||
95 | if (S_ISDIR(s.st_mode)) { | ||
96 | // create directory | ||
97 | int rv = mkdir(path, 0755); | ||
98 | if (rv == -1) | ||
99 | errExit("mkdir"); | ||
100 | |||
101 | } | ||
102 | |||
103 | // process regular file | ||
104 | else { | ||
105 | // create an empty file | ||
106 | FILE *fp = fopen(path, "w"); | ||
107 | if (!fp) { | ||
108 | fprintf(stderr, "Error: cannot create empty file in home directory\n"); | ||
109 | exit(1); | ||
110 | } | ||
111 | fclose(fp); | ||
112 | } | ||
113 | |||
114 | // set file properties | ||
115 | if (chown(path, s.st_uid, s.st_gid) < 0) | ||
116 | errExit("chown"); | ||
117 | if (chmod(path, s.st_mode) < 0) | ||
118 | errExit("chmod"); | ||
119 | |||
120 | // mount | ||
121 | if (mount(wfile, path, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
122 | errExit("mount bind"); | ||
123 | |||
124 | free(wfile); | ||
125 | } | ||
126 | |||
127 | |||
128 | // whitelist for /home/user directory | ||
129 | void fs_whitelist(void) { | ||
130 | char *homedir = cfg.homedir; | ||
131 | assert(homedir); | ||
132 | ProfileEntry *entry = cfg.profile; | ||
133 | if (!entry) | ||
134 | return; | ||
135 | |||
136 | // realpath function will fail with ENOENT if the file is not found | ||
137 | // we need to expand the path before installing a new, empty home directory | ||
138 | while (entry) { | ||
139 | // handle only whitelist commands | ||
140 | if (strncmp(entry->data, "whitelist ", 10)) { | ||
141 | entry = entry->next; | ||
142 | continue; | ||
143 | } | ||
144 | |||
145 | char *new_name = expand_home(entry->data + 10, cfg.homedir); | ||
146 | assert(new_name); | ||
147 | char *fname = realpath(new_name, NULL); | ||
148 | free(new_name); | ||
149 | if (fname) { | ||
150 | // change file name in entry->data | ||
151 | if (strcmp(fname, entry->data + 10) != 0) { | ||
152 | char *newdata; | ||
153 | if (asprintf(&newdata, "whitelist %s", fname) == -1) | ||
154 | errExit("asprintf"); | ||
155 | entry->data = newdata; | ||
156 | if (arg_debug) | ||
157 | printf("Replaced whitelist path: %s\n", entry->data); | ||
158 | } | ||
159 | |||
160 | free(fname); | ||
161 | } | ||
162 | else { | ||
163 | // file not found, blank the entry in the list | ||
164 | if (arg_debug) | ||
165 | printf("Removed whitelist path: %s\n", entry->data); | ||
166 | *entry->data = '\0'; | ||
167 | } | ||
168 | entry = entry->next; | ||
169 | } | ||
170 | |||
171 | // create /tmp/firejail/mnt/whome directory | ||
172 | fs_build_mnt_dir(); | ||
173 | int rv = mkdir(WHITELIST_HOME_DIR, S_IRWXU | S_IRWXG | S_IRWXO); | ||
174 | if (rv == -1) | ||
175 | errExit("mkdir"); | ||
176 | if (chown(WHITELIST_HOME_DIR, getuid(), getgid()) < 0) | ||
177 | errExit("chown"); | ||
178 | if (chmod(WHITELIST_HOME_DIR, 0755) < 0) | ||
179 | errExit("chmod"); | ||
180 | |||
181 | // keep a copy of real home dir in /tmp/firejail/mnt/whome | ||
182 | if (mount(cfg.homedir, WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
183 | errExit("mount bind"); | ||
184 | |||
185 | // start building the new home directory by mounting a tmpfs fielsystem | ||
186 | fs_private(); | ||
187 | |||
188 | // go through profile rules again, and interpret whitelist commands | ||
189 | entry = cfg.profile; | ||
190 | while (entry) { | ||
191 | // handle only whitelist commands | ||
192 | if (strncmp(entry->data, "whitelist ", 10)) { | ||
193 | entry = entry->next; | ||
194 | continue; | ||
195 | } | ||
196 | |||
197 | whitelist_path(entry->data + 10); | ||
198 | |||
199 | entry = entry->next; | ||
200 | } | ||
201 | |||
202 | // mask the real home directory, currently mounted on /tmp/firejail/mnt/whome | ||
203 | if (mount("tmpfs", WHITELIST_HOME_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
204 | errExit("mount tmpfs"); | ||
205 | } | ||