diff options
author | netblue30 <netblue30@yahoo.com> | 2015-08-08 19:12:30 -0400 |
---|---|---|
committer | netblue30 <netblue30@yahoo.com> | 2015-08-08 19:12:30 -0400 |
commit | 1379851360349d6617ad32944a25ee5e2bb74fc2 (patch) | |
tree | f69b48e90708bfa3c2723d5a27ed3e024c827b43 /src/firejail/fs_var.c | |
parent | delete files (diff) | |
download | firejail-1379851360349d6617ad32944a25ee5e2bb74fc2.tar.gz firejail-1379851360349d6617ad32944a25ee5e2bb74fc2.tar.zst firejail-1379851360349d6617ad32944a25ee5e2bb74fc2.zip |
Baseline firejail 0.9.28
Diffstat (limited to 'src/firejail/fs_var.c')
-rw-r--r-- | src/firejail/fs_var.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/src/firejail/fs_var.c b/src/firejail/fs_var.c new file mode 100644 index 000000000..ee0f81828 --- /dev/null +++ b/src/firejail/fs_var.c | |||
@@ -0,0 +1,388 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) | ||
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 <glob.h> | ||
25 | #include <dirent.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <pwd.h> | ||
28 | #include <utmp.h> | ||
29 | #include <time.h> | ||
30 | |||
31 | typedef struct dirdata_t{ | ||
32 | struct dirdata_t *next; | ||
33 | char *name; | ||
34 | mode_t st_mode; | ||
35 | uid_t st_uid; | ||
36 | gid_t st_gid; | ||
37 | } DirData; | ||
38 | |||
39 | static DirData *dirlist = NULL; | ||
40 | |||
41 | static void release_all(void) { | ||
42 | DirData *ptr = dirlist; | ||
43 | while (ptr) { | ||
44 | DirData *next = ptr->next; | ||
45 | free(ptr->name); | ||
46 | free(ptr); | ||
47 | ptr = next; | ||
48 | } | ||
49 | dirlist = NULL; | ||
50 | } | ||
51 | |||
52 | static void build_list(const char *srcdir) { | ||
53 | // extract current /var/log directory data | ||
54 | struct dirent *dir; | ||
55 | DIR *d = opendir(srcdir); | ||
56 | if (d == NULL) | ||
57 | return; | ||
58 | |||
59 | while ((dir = readdir(d))) { | ||
60 | if(strcmp(dir->d_name, "." ) == 0 || strcmp(dir->d_name, ".." ) == 0) | ||
61 | continue; | ||
62 | |||
63 | if (dir->d_type == DT_DIR ) { | ||
64 | // get properties | ||
65 | struct stat s; | ||
66 | char *name; | ||
67 | if (asprintf(&name, "%s/%s", srcdir, dir->d_name) == -1) | ||
68 | continue; | ||
69 | if (stat(name, &s) == -1) | ||
70 | continue; | ||
71 | if (S_ISLNK(s.st_mode)) { | ||
72 | free(name); | ||
73 | continue; | ||
74 | } | ||
75 | |||
76 | // printf("directory %u %u:%u %s\n", | ||
77 | // s.st_mode, | ||
78 | // s.st_uid, | ||
79 | // s.st_gid, | ||
80 | // dir->d_name); | ||
81 | |||
82 | DirData *ptr = malloc(sizeof(DirData)); | ||
83 | if (ptr == NULL) | ||
84 | errExit("malloc"); | ||
85 | memset(ptr, 0, sizeof(DirData)); | ||
86 | ptr->name = name; | ||
87 | ptr->st_mode = s.st_mode; | ||
88 | ptr->st_uid = s.st_uid; | ||
89 | ptr->st_gid = s.st_gid; | ||
90 | ptr->next = dirlist; | ||
91 | dirlist = ptr; | ||
92 | } | ||
93 | } | ||
94 | closedir(d); | ||
95 | } | ||
96 | |||
97 | static void build_dirs(void) { | ||
98 | // create directories under /var/log | ||
99 | DirData *ptr = dirlist; | ||
100 | while (ptr) { | ||
101 | if (mkdir(ptr->name, ptr->st_mode)) | ||
102 | errExit("mkdir"); | ||
103 | if (chown(ptr->name, ptr->st_uid, ptr->st_gid)) | ||
104 | errExit("chown"); | ||
105 | ptr = ptr->next; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | void fs_var_log(void) { | ||
110 | build_list("/var/log"); | ||
111 | |||
112 | // create /var/log if it does't exit | ||
113 | if (is_dir("/var/log")) { | ||
114 | // extract group id for /var/log/wtmp | ||
115 | struct stat s; | ||
116 | gid_t wtmp_group = 0; | ||
117 | if (stat("/var/log/wtmp", &s) == 0) | ||
118 | wtmp_group = s.st_gid; | ||
119 | |||
120 | // mount a tmpfs on top of /var/log | ||
121 | if (arg_debug) | ||
122 | printf("Mounting tmpfs on /var/log\n"); | ||
123 | if (mount("tmpfs", "/var/log", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
124 | errExit("mounting /var/log"); | ||
125 | |||
126 | build_dirs(); | ||
127 | release_all(); | ||
128 | |||
129 | // create an empty /var/log/wtmp file | ||
130 | /* coverity[toctou] */ | ||
131 | FILE *fp = fopen("/var/log/wtmp", "w"); | ||
132 | if (fp) | ||
133 | fclose(fp); | ||
134 | if (chown("/var/log/wtmp", 0, wtmp_group) < 0) | ||
135 | errExit("chown"); | ||
136 | if (chmod("/var/log/wtmp", S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH ) < 0) | ||
137 | errExit("chmod"); | ||
138 | |||
139 | // create an empty /var/log/btmp file | ||
140 | fp = fopen("/var/log/btmp", "w"); | ||
141 | if (fp) | ||
142 | fclose(fp); | ||
143 | if (chown("/var/log/btmp", 0, wtmp_group) < 0) | ||
144 | errExit("chown"); | ||
145 | if (chmod("/var/log/btmp", S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP) < 0) | ||
146 | errExit("chmod"); | ||
147 | } | ||
148 | else | ||
149 | fprintf(stderr, "Warning: cannot mount tmpfs in top of /var/log\n"); | ||
150 | } | ||
151 | |||
152 | void fs_var_lib(void) { | ||
153 | struct stat s; | ||
154 | |||
155 | // ISC DHCP multiserver | ||
156 | if (stat("/var/lib/dhcp", &s) == 0) { | ||
157 | if (arg_debug) | ||
158 | printf("Mounting tmpfs on /var/lib/dhcp\n"); | ||
159 | if (mount("tmpfs", "/var/lib/dhcp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
160 | errExit("mounting /var/lib/dhcp"); | ||
161 | |||
162 | // isc dhcp server requires a /var/lib/dhcp/dhcpd.leases file | ||
163 | FILE *fp = fopen("/var/lib/dhcp/dhcpd.leases", "w"); | ||
164 | |||
165 | if (fp) { | ||
166 | fprintf(fp, "\n"); | ||
167 | fclose(fp); | ||
168 | if (chown("/var/lib/dhcp/dhcpd.leases", 0, 0) == -1) | ||
169 | errExit("chown"); | ||
170 | if (chmod("/var/lib/dhcp/dhcpd.leases", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) | ||
171 | errExit("chmod"); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | // nginx multiserver | ||
176 | if (stat("/var/lib/nginx", &s) == 0) { | ||
177 | if (arg_debug) | ||
178 | printf("Mounting tmpfs on /var/lib/nginx\n"); | ||
179 | if (mount("tmpfs", "/var/lib/nginx", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
180 | errExit("mounting /var/lib/nginx"); | ||
181 | } | ||
182 | |||
183 | // net-snmp multiserver | ||
184 | if (stat("/var/lib/snmp", &s) == 0) { | ||
185 | if (arg_debug) | ||
186 | printf("Mounting tmpfs on /var/lib/snmp\n"); | ||
187 | if (mount("tmpfs", "/var/lib/snmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
188 | errExit("mounting /var/lib/snmp"); | ||
189 | } | ||
190 | |||
191 | // this is where sudo remembers its state | ||
192 | if (stat("/var/lib/sudo", &s) == 0) { | ||
193 | if (arg_debug) | ||
194 | printf("Mounting tmpfs on /var/lib/sudo\n"); | ||
195 | if (mount("tmpfs", "/var/lib/sudo", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
196 | errExit("mounting /var/lib/sudo"); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | void fs_var_cache(void) { | ||
201 | struct stat s; | ||
202 | |||
203 | if (stat("/var/cache/apache2", &s) == 0) { | ||
204 | if (arg_debug) | ||
205 | printf("Mounting tmpfs on /var/cache/apache2\n"); | ||
206 | if (mount("tmpfs", "/var/cache/apache2", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
207 | errExit("mounting /var/cahce/apache2"); | ||
208 | } | ||
209 | |||
210 | if (stat("/var/cache/lighttpd", &s) == 0) { | ||
211 | if (arg_debug) | ||
212 | printf("Mounting tmpfs on /var/cache/lighttpd\n"); | ||
213 | if (mount("tmpfs", "/var/cache/lighttpd", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
214 | errExit("mounting /var/cache/lighttpd"); | ||
215 | |||
216 | struct passwd *p = getpwnam("www-data"); | ||
217 | uid_t uid = 0; | ||
218 | gid_t gid = 0; | ||
219 | if (p) { | ||
220 | uid = p->pw_uid; | ||
221 | gid = p->pw_gid; | ||
222 | } | ||
223 | |||
224 | int rv = mkdir("/var/cache/lighttpd/compress", S_IRWXU | S_IRWXG | S_IRWXO); | ||
225 | if (rv == -1) | ||
226 | errExit("mkdir"); | ||
227 | if (chown("/var/cache/lighttpd/compress", uid, gid) < 0) | ||
228 | errExit("chown"); | ||
229 | |||
230 | rv = mkdir("/var/cache/lighttpd/uploads", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); | ||
231 | if (rv == -1) | ||
232 | errExit("mkdir"); | ||
233 | if (chown("/var/cache/lighttpd/uploads", uid, gid) < 0) | ||
234 | errExit("chown"); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | void dbg_test_dir(const char *dir) { | ||
239 | if (arg_debug) { | ||
240 | if (is_dir(dir)) | ||
241 | printf("%s is a directory\n", dir); | ||
242 | if (is_link(dir)) { | ||
243 | char *lnk = get_link(dir); | ||
244 | if (lnk) { | ||
245 | printf("%s is a symbolic link to %s\n", dir, lnk); | ||
246 | free(lnk); | ||
247 | } | ||
248 | } | ||
249 | } | ||
250 | } | ||
251 | |||
252 | |||
253 | void fs_var_lock(void) { | ||
254 | |||
255 | if (is_dir("/var/lock")) { | ||
256 | if (arg_debug) | ||
257 | printf("Mounting tmpfs on /var/lock\n"); | ||
258 | if (mount("tmpfs", "/var/lock", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) | ||
259 | errExit("mounting /lock"); | ||
260 | } | ||
261 | else { | ||
262 | char *lnk = get_link("/var/lock"); | ||
263 | if (lnk) { | ||
264 | // convert a link such as "../shm" into "/shm" | ||
265 | char *lnk2 = lnk; | ||
266 | int cnt = 0; | ||
267 | while (strncmp(lnk2, "../", 3) == 0) { | ||
268 | cnt++; | ||
269 | lnk2 = lnk2 + 3; | ||
270 | } | ||
271 | if (cnt != 0) | ||
272 | lnk2 = lnk + (cnt - 1) * 3 + 2; | ||
273 | |||
274 | if (!is_dir(lnk2)) { | ||
275 | // create directory | ||
276 | if (mkdir(lnk2, S_IRWXU|S_IRWXG|S_IRWXO)) | ||
277 | errExit("mkdir"); | ||
278 | if (chown(lnk2, 0, 0)) | ||
279 | errExit("chown"); | ||
280 | if (chmod(lnk2, S_IRWXU|S_IRWXG|S_IRWXO)) | ||
281 | errExit("chmod"); | ||
282 | } | ||
283 | if (arg_debug) | ||
284 | printf("Mounting tmpfs on %s on behalf of /var/lock\n", lnk2); | ||
285 | if (mount("tmpfs", lnk2, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) | ||
286 | errExit("mounting /var/lock"); | ||
287 | free(lnk); | ||
288 | } | ||
289 | else { | ||
290 | fprintf(stderr, "Warning: /var/lock not mounted\n"); | ||
291 | dbg_test_dir("/var/lock"); | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | |||
296 | void fs_var_tmp(void) { | ||
297 | |||
298 | if (!is_link("/var/tmp")) { | ||
299 | if (arg_debug) | ||
300 | printf("Mounting tmpfs on /var/tmp\n"); | ||
301 | if (mount("tmpfs", "/var/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) | ||
302 | errExit("mounting /var/tmp"); | ||
303 | } | ||
304 | else { | ||
305 | fprintf(stderr, "Warning: /var/tmp not mounted\n"); | ||
306 | dbg_test_dir("/var/tmp"); | ||
307 | } | ||
308 | } | ||
309 | |||
310 | void fs_var_utmp(void) { | ||
311 | struct stat s; | ||
312 | |||
313 | // extract utmp group id | ||
314 | gid_t utmp_group = 0; | ||
315 | if (stat("/var/run/utmp", &s) == 0) | ||
316 | utmp_group = s.st_gid; | ||
317 | else { | ||
318 | fprintf(stderr, "Warning: cannot find /var/run/utmp\n"); | ||
319 | return; | ||
320 | } | ||
321 | |||
322 | // create /tmp/firejail/mnt directory | ||
323 | fs_build_mnt_dir(); | ||
324 | |||
325 | // create a new utmp file | ||
326 | if (arg_debug) | ||
327 | printf("Create the new utmp file\n"); | ||
328 | char *utmp; | ||
329 | if (asprintf(&utmp, "%s/utmp", MNT_DIR) == -1) | ||
330 | errExit("asprintf"); | ||
331 | FILE *fp = fopen(utmp, "w"); | ||
332 | if (!fp) | ||
333 | errExit("fopen"); | ||
334 | |||
335 | // read current utmp | ||
336 | struct utmp *u; | ||
337 | struct utmp u_boot; | ||
338 | setutent(); | ||
339 | while ((u = getutent()) != NULL) { | ||
340 | if (u->ut_type == BOOT_TIME) { | ||
341 | memcpy(&u_boot, u, sizeof(u_boot)); | ||
342 | u_boot.ut_tv.tv_sec = (unsigned) time(NULL); | ||
343 | } | ||
344 | } | ||
345 | endutent(); | ||
346 | |||
347 | // save new utmp file | ||
348 | fwrite(&u_boot, sizeof(u_boot), 1, fp); | ||
349 | fclose(fp); | ||
350 | if (chown(utmp, 0, utmp_group) < 0) | ||
351 | errExit("chown"); | ||
352 | if (chmod(utmp, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH ) < 0) | ||
353 | errExit("chmod"); | ||
354 | |||
355 | // mount the new utmp file | ||
356 | if (arg_debug) | ||
357 | printf("Mount the new utmp file\n"); | ||
358 | if (mount(utmp, "/var/run/utmp", NULL, MS_BIND|MS_REC, NULL) < 0) | ||
359 | errExit("mount bind utmp"); | ||
360 | } | ||
361 | |||
362 | |||
363 | #if 0 | ||
364 | Testing servers: | ||
365 | |||
366 | brctl addbr br0 | ||
367 | ifconfig br0 10.10.20.1/24 | ||
368 | |||
369 | apt-get install snmpd | ||
370 | insserv -r snmpd | ||
371 | sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/snmpd start; sleep inf" | ||
372 | |||
373 | apt-get install apache2 | ||
374 | insserv -r apache2 | ||
375 | sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/apache2 start; sleep inf" | ||
376 | |||
377 | apt-get install nginx | ||
378 | insserv -r nginx | ||
379 | sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/nginx start; sleep inf" | ||
380 | |||
381 | apt-get install lighttpd | ||
382 | insserv -r lighttpd | ||
383 | sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/lighttpd start; sleep inf" | ||
384 | |||
385 | apt-get install isc-dhcp-server | ||
386 | insserv -r isc-dhcp-server | ||
387 | sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/isc-dhcp-server start; sleep inf" | ||
388 | #endif | ||