aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Igor Bukanov <igor@mir2.org>2017-01-29 18:13:30 +0100
committerLibravatar Igor Bukanov <igor@mir2.org>2017-01-29 18:13:30 +0100
commit5292798bb4fffc2f8c9b6de2bf373cf86ebf8e3b (patch)
tree28c099109c594d49f455be3cd437ff2bc780b651
parentsupport allow-private-blacklist in profile files (diff)
downloadfirejail-5292798bb4fffc2f8c9b6de2bf373cf86ebf8e3b.tar.gz
firejail-5292798bb4fffc2f8c9b6de2bf373cf86ebf8e3b.tar.zst
firejail-5292798bb4fffc2f8c9b6de2bf373cf86ebf8e3b.zip
fixing --hosts-file privelege check
Currently the code uses the access() call to check if the user has an access to a file that is copied into the root as /etc/hosts. This inevitably adds a race when the user changes the file to a symbolic link pointing to an arbitrary location on the filsystem after the access check is done but before opening the file to copy it. This potentially allows to read any file on the system. To close this the code adds a utility copy_file_from_user_to_root . It opens the copy destination file as root and then forks/drop privileges. Then as a user the utility opens the source file and do the copy into the destination descriptor that is preserved accross the fork.
-rw-r--r--src/firejail/firejail.h1
-rw-r--r--src/firejail/fs_hostname.c2
-rw-r--r--src/firejail/util.c91
3 files changed, 68 insertions, 26 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 0f836f1db..7d6e16094 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -453,6 +453,7 @@ void logargs(int argc, char **argv) ;
453void logerr(const char *msg); 453void logerr(const char *msg);
454int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 454int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
455void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 455void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
456void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
456void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode); 457void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode);
457int is_dir(const char *fname); 458int is_dir(const char *fname);
458int is_link(const char *fname); 459int is_link(const char *fname);
diff --git a/src/firejail/fs_hostname.c b/src/firejail/fs_hostname.c
index 19af11dd8..3b586b276 100644
--- a/src/firejail/fs_hostname.c
+++ b/src/firejail/fs_hostname.c
@@ -147,7 +147,7 @@ errexit:
147} 147}
148 148
149void fs_store_hosts_file(void) { 149void fs_store_hosts_file(void) {
150 copy_file(cfg.hosts_file, RUN_HOSTS_FILE, 0, 0, 0644); // root needed 150 copy_file_from_user_to_root(cfg.hosts_file, RUN_HOSTS_FILE, 0, 0, 0644); // root needed
151} 151}
152 152
153void fs_mount_hosts_file(void) { 153void fs_mount_hosts_file(void) {
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 10000e912..44891ce2d 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -169,6 +169,25 @@ void logerr(const char *msg) {
169 closelog(); 169 closelog();
170} 170}
171 171
172static int copy_file_by_fd(int src, int dst) {
173 assert(src >= 0);
174 assert(dst >= 0);
175
176 ssize_t len;
177 static const int BUFLEN = 1024;
178 unsigned char buf[BUFLEN];
179 while ((len = read(src, buf, BUFLEN)) > 0) {
180 int done = 0;
181 while (done != len) {
182 int rv = write(dst, buf + done, len - done);
183 if (rv == -1)
184 return -1;
185 done += rv;
186 }
187 }
188 fflush(0);
189 return 0;
190}
172 191
173// return -1 if error, 0 if no error; if destname already exists, return error 192// return -1 if error, 0 if no error; if destname already exists, return error
174int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) { 193int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) {
@@ -190,33 +209,16 @@ int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, m
190 return -1; 209 return -1;
191 } 210 }
192 211
193 // copy 212 int errors = copy_file_by_fd(src, dst);
194 ssize_t len; 213 if (!errors) {
195 static const int BUFLEN = 1024; 214 if (fchown(dst, uid, gid) == -1)
196 unsigned char buf[BUFLEN]; 215 errExit("fchown");
197 while ((len = read(src, buf, BUFLEN)) > 0) { 216 if (fchmod(dst, mode) == -1)
198 int done = 0; 217 errExit("fchmod");
199 while (done != len) {
200 int rv = write(dst, buf + done, len - done);
201 if (rv == -1) {
202 close(src);
203 close(dst);
204 return -1;
205 }
206
207 done += rv;
208 }
209 } 218 }
210 fflush(0);
211
212 if (fchown(dst, uid, gid) == -1)
213 errExit("fchown");
214 if (fchmod(dst, mode) == -1)
215 errExit("fchmod");
216
217 close(src); 219 close(src);
218 close(dst); 220 close(dst);
219 return 0; 221 return errors;
220} 222}
221 223
222// return -1 if error, 0 if no error 224// return -1 if error, 0 if no error
@@ -241,6 +243,45 @@ void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid
241 waitpid(child, NULL, 0); 243 waitpid(child, NULL, 0);
242} 244}
243 245
246void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) {
247 // open destination
248 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
249 if (dst < 0) {
250 fprintf(stderr, "Warning: cannot open destination file %s, file not copied\n", destname);
251 return;
252 }
253
254 pid_t child = fork();
255 if (child < 0)
256 errExit("fork");
257 if (child == 0) {
258 // drop privileges
259 drop_privs(0);
260
261 int src = open(srcname, O_RDONLY);
262 if (src < 0) {
263 fprintf(stderr, "Warning: cannot open source file %s, file not copied\n", srcname);
264 } else {
265 if (copy_file_by_fd(src, dst)) {
266 fprintf(stderr, "Warning: cannot copy %s\n", srcname);
267 }
268 close(src);
269 }
270 close(dst);
271#ifdef HAVE_GCOV
272 __gcov_flush();
273#endif
274 _exit(0);
275 }
276 // wait for the child to finish
277 waitpid(child, NULL, 0);
278 if (fchown(dst, uid, gid) == -1)
279 errExit("fchown");
280 if (fchmod(dst, mode) == -1)
281 errExit("fchmod");
282 close(dst);
283}
284
244// return -1 if error, 0 if no error 285// return -1 if error, 0 if no error
245void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode) { 286void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode) {
246 pid_t child = fork(); 287 pid_t child = fork();
@@ -864,4 +905,4 @@ errexit:
864 close(fd); 905 close(fd);
865 fprintf(stderr, "Error: cannot read %s\n", fname); 906 fprintf(stderr, "Error: cannot read %s\n", fname);
866 exit(1); 907 exit(1);
867} \ No newline at end of file 908}