aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/restrict_users.c
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@yahoo.com>2015-11-19 10:00:57 -0500
committerLibravatar netblue30 <netblue30@yahoo.com>2015-11-19 10:00:57 -0500
commit4f003daec3ed3acd4dfbe64138d0a627210c8bdf (patch)
tree09356109fc420c7abc8f2d448fcbe6e1286d061e /src/firejail/restrict_users.c
parentadmin (diff)
downloadfirejail-4f003daec3ed3acd4dfbe64138d0a627210c8bdf.tar.gz
firejail-4f003daec3ed3acd4dfbe64138d0a627210c8bdf.tar.zst
firejail-4f003daec3ed3acd4dfbe64138d0a627210c8bdf.zip
prevent leaking user information by modifying /home directory, /etc/passwd and /etc/group
Diffstat (limited to 'src/firejail/restrict_users.c')
-rw-r--r--src/firejail/restrict_users.c328
1 files changed, 328 insertions, 0 deletions
diff --git a/src/firejail/restrict_users.c b/src/firejail/restrict_users.c
new file mode 100644
index 000000000..c0a14ff6f
--- /dev/null
+++ b/src/firejail/restrict_users.c
@@ -0,0 +1,328 @@
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#define MAXBUF 1024
31
32// linked list of users
33typedef struct user_list {
34 struct user_list *next;
35 const char *user;
36} USER_LIST;
37USER_LIST *ulist = NULL;
38
39static void ulist_add(const char *user) {
40 assert(user);
41
42 USER_LIST *nlist = malloc(sizeof(USER_LIST));
43 memset(nlist, 0, sizeof(USER_LIST));
44 nlist->user = user;
45 nlist->next = ulist;
46 ulist = nlist;
47}
48
49static USER_LIST *ulist_find(const char *user) {
50 assert(user);
51
52 USER_LIST *ptr = ulist;
53 while (ptr) {
54 if (strcmp(ptr->user, user) == 0)
55 return ptr;
56 ptr = ptr->next;
57 }
58
59 return NULL;
60}
61
62static void sanitize_home(void) {
63 assert(getuid() != 0); // this code works only for regular users
64
65 if (arg_debug)
66 printf("Cleaning /home directory\n");
67
68 struct stat s;
69 if (stat(cfg.homedir, &s) == -1) {
70 // cannot find home directory, just return
71 fprintf(stderr, "Warning: cannot find home directory\n");
72 return;
73 }
74
75 fs_build_mnt_dir();
76 if (mkdir(WHITELIST_HOME_DIR, 0755) == -1)
77 errExit("mkdir");
78
79 // keep a copy of the user home directory
80 if (mount(cfg.homedir, WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
81 errExit("mount bind");
82
83 // mount tmpfs in the new home
84 if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
85 errExit("mount tmpfs");
86
87 // create user home directory
88 if (mkdir(cfg.homedir, 0755) == -1)
89 errExit("mkdir");
90
91 // set mode and ownership
92 if (chown(cfg.homedir, s.st_uid, s.st_gid) == -1)
93 errExit("chown");
94 if (chmod(cfg.homedir, s.st_mode) == -1)
95 errExit("chmod");
96
97 // mount user home directory
98 if (mount(WHITELIST_HOME_DIR, cfg.homedir, NULL, MS_BIND|MS_REC, NULL) < 0)
99 errExit("mount bind");
100
101 // mask home dir under /run
102 if (mount("tmpfs", WHITELIST_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
103 errExit("mount tmpfs");
104}
105
106static void sanitize_passwd(void) {
107 struct stat s;
108 if (stat("/etc/passwd", &s) == -1)
109 return;
110 if (arg_debug)
111 printf("Sanitizing /etc/passwd\n");
112
113 FILE *fpin = NULL;
114 FILE *fpout = NULL;
115 fs_build_mnt_dir();
116
117 // open files
118 fpin = fopen("/etc/passwd", "r");
119 if (!fpin)
120 goto errout;
121 fpout = fopen(PASSWD_FILE, "w");
122 if (!fpout)
123 goto errout;
124
125 // read the file line by line
126 char buf[MAXBUF];
127 uid_t myuid = getuid();
128 while (fgets(buf, MAXBUF, fpin)) {
129 // comments and empty lines
130 if (*buf == '\0' || *buf == '#')
131 continue;
132
133 // sample line:
134 // www-data:x:33:33:www-data:/var/www:/bin/sh
135 // drop lines with uid > 1000 and not the current user
136 char *ptr = buf;
137
138 // advance to uid
139 while (*ptr != ':' && *ptr != '\0')
140 ptr++;
141 if (*ptr == '\0')
142 goto errout;
143 char *ptr1 = ptr;
144 ptr++;
145 while (*ptr != ':' && *ptr != '\0')
146 ptr++;
147 if (*ptr == '\0')
148 goto errout;
149 ptr++;
150 if (*ptr == '\0')
151 goto errout;
152
153 // process uid
154 int uid;
155 int rv = sscanf(ptr, "%d:", &uid);
156 if (rv == 0 || uid < 0)
157 goto errout;
158 if (uid < 1000) { // todo extract UID_MIN from /etc/login.def
159 fprintf(fpout, "%s", buf);
160 continue;
161 }
162 if ((uid_t) uid != myuid) {
163 // store user name - necessary to process /etc/group
164 *ptr1 = '\0';
165 char *user = strdup(buf);
166 if (!user)
167 errExit("malloc");
168 ulist_add(user);
169 continue; // skip line
170 }
171 fprintf(fpout, "%s", buf);
172 }
173 fclose(fpin);
174 fclose(fpout);
175 if (chown(PASSWD_FILE, 0, 0) == -1)
176 errExit("chown");
177 if (chmod(PASSWD_FILE, 0644) == -1)
178 errExit("chmod");
179
180 // mount-bind tne new password file
181 if (mount(PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0)
182 errExit("mount");
183
184 return;
185
186errout:
187 fprintf(stderr, "Warning: failed to clean up /etc/passwd\n");
188 if (fpin)
189 fclose(fpin);
190 if (fpout)
191 fclose(fpout);
192}
193
194// returns 1 if fails, 0 if OK
195static int copy_line(FILE *fpout, char *buf, char *ptr) {
196 // fpout: GROUP_FILE
197 // buf: pulse:x:115:netblue,bingo
198 // ptr: 115:neblue,bingo
199
200 while (*ptr != ':' && *ptr != '\0')
201 ptr++;
202 if (*ptr == '\0')
203 return 1;
204
205 ptr++;
206 if (*ptr == '\n' || *ptr == '\0') {
207 fprintf(fpout, "%s", buf);
208 return 0;
209 }
210
211 // print what we have so far
212 char tmp = *ptr;
213 *ptr = '\0';
214 fprintf(fpout, "%s", buf);
215 *ptr = tmp;
216
217 // tokenize
218 char *token = strtok(ptr, ",\n");
219 int first = 1;
220 while (token) {
221 char *newtoken = strtok(NULL, ",\n");
222 if (ulist_find(token)) {
223 //skip
224 token = newtoken;
225 continue;
226 }
227 if (!first)
228 fprintf(fpout, ",");
229 first = 0;
230 fprintf(fpout, "%s", token);
231 token = newtoken;
232 }
233 fprintf(fpout, "\n");
234 return 0;
235}
236
237static void sanitize_group(void) {
238 struct stat s;
239 if (stat("/etc/group", &s) == -1)
240 return;
241 if (arg_debug)
242 printf("Sanitizing /etc/group\n");
243
244 FILE *fpin = NULL;
245 FILE *fpout = NULL;
246 fs_build_mnt_dir();
247
248 // open files
249 fpin = fopen("/etc/group", "r");
250 if (!fpin)
251 goto errout;
252 fpout = fopen(GROUP_FILE, "w");
253 if (!fpout)
254 goto errout;
255
256 // read the file line by line
257 char buf[MAXBUF];
258 gid_t mygid = getgid();
259 while (fgets(buf, MAXBUF, fpin)) {
260 // comments and empty lines
261 if (*buf == '\0' || *buf == '#')
262 continue;
263
264 // sample line:
265 // pulse:x:115:netblue,bingo
266 // drop lines with uid > 1000 and not the current user group
267 char *ptr = buf;
268
269 // advance to uid
270 while (*ptr != ':' && *ptr != '\0')
271 ptr++;
272 if (*ptr == '\0')
273 goto errout;
274 ptr++;
275 while (*ptr != ':' && *ptr != '\0')
276 ptr++;
277 if (*ptr == '\0')
278 goto errout;
279 ptr++;
280 if (*ptr == '\0')
281 goto errout;
282
283 // process uid
284 int gid;
285 int rv = sscanf(ptr, "%d:", &gid);
286 if (rv == 0 || gid < 0)
287 goto errout;
288 if (gid < 1000) { // todo extract GID_MIN from /etc/login.def
289 if (copy_line(fpout, buf, ptr))
290 goto errout;
291 continue;
292 }
293 if ((gid_t) gid != mygid) {
294 continue; // skip line
295 }
296 fprintf(fpout, "%s", buf);
297 if (copy_line(fpout, buf, ptr))
298 goto errout;
299 }
300 fclose(fpin);
301 fclose(fpout);
302 if (chown(GROUP_FILE, 0, 0) == -1)
303 errExit("chown");
304 if (chmod(GROUP_FILE, 0644) == -1)
305 errExit("chmod");
306
307 // mount-bind tne new group file
308 if (mount(GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0)
309 errExit("mount");
310
311 return;
312
313errout:
314 fprintf(stderr, "Warning: failed to clean up /etc/group\n");
315 if (fpin)
316 fclose(fpin);
317 if (fpout)
318 fclose(fpout);
319}
320
321void restrict_users(void) {
322 // only in user mode
323 if (getuid()) {
324 sanitize_home();
325 sanitize_passwd();
326 sanitize_group();
327 }
328}