aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2018-06-04 14:09:39 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2018-06-04 14:09:39 +0200
commitdf5c9f035ac3353ac1bfb9ceda71cbf61e8cac81 (patch)
treeecb7c9e9f52b0db3027f4216af85dfe51775afe5
parentMerge pull request #1977 from pizzadude/patch-10 (diff)
downloadfirejail-df5c9f035ac3353ac1bfb9ceda71cbf61e8cac81.tar.gz
firejail-df5c9f035ac3353ac1bfb9ceda71cbf61e8cac81.tar.zst
firejail-df5c9f035ac3353ac1bfb9ceda71cbf61e8cac81.zip
add private-cache option
implementation is based on an idea of James Henstridge, Canonical
-rw-r--r--src/firejail/firejail.h3
-rw-r--r--src/firejail/fs.c53
-rw-r--r--src/firejail/main.c6
-rw-r--r--src/firejail/profile.c4
-rw-r--r--src/firejail/sandbox.c9
-rw-r--r--src/firejail/util.c50
6 files changed, 124 insertions, 1 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index f988dc114..18d66b983 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -308,6 +308,7 @@ static inline int any_interface_configured(void) {
308 308
309extern int arg_private; // mount private /home 309extern int arg_private; // mount private /home
310extern int arg_private_template; // private /home template 310extern int arg_private_template; // private /home template
311extern int arg_private_cache; // private home/.cache
311extern int arg_debug; // print debug messages 312extern int arg_debug; // print debug messages
312extern int arg_debug_blacklists; // print debug messages for blacklists 313extern int arg_debug_blacklists; // print debug messages for blacklists
313extern int arg_debug_whitelists; // print debug messages for whitelists 314extern int arg_debug_whitelists; // print debug messages for whitelists
@@ -447,6 +448,7 @@ void fs_overlayfs(void);
447void fs_chroot(const char *rootdir); 448void fs_chroot(const char *rootdir);
448void fs_check_chroot_dir(const char *rootdir); 449void fs_check_chroot_dir(const char *rootdir);
449void fs_private_tmp(void); 450void fs_private_tmp(void);
451void fs_private_cache(void);
450 452
451// profile.c 453// profile.c
452// find and read the profile specified by name from dir directory 454// find and read the profile specified by name from dir directory
@@ -525,6 +527,7 @@ void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid);
525unsigned extract_timeout(const char *str); 527unsigned extract_timeout(const char *str);
526void disable_file_or_dir(const char *fname); 528void disable_file_or_dir(const char *fname);
527void disable_file_path(const char *path, const char *file); 529void disable_file_path(const char *path, const char *file);
530int safe_fd(const char *path, int flags);
528 531
529// Get info regarding the last kernel mount operation from /proc/self/mountinfo 532// Get info regarding the last kernel mount operation from /proc/self/mountinfo
530// The return value points to a static area, and will be overwritten by subsequent calls. 533// The return value points to a static area, and will be overwritten by subsequent calls.
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index 0562c7424..8db45f967 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -1340,3 +1340,56 @@ void fs_private_tmp(void) {
1340 1340
1341 1341
1342} 1342}
1343
1344// this function is called from sandbox.c before blacklist/whitelist functions
1345void fs_private_cache(void) {
1346 char *cache;
1347 if (asprintf(&cache, "%s/.cache", cfg.homedir) == -1)
1348 errExit("asprintf");
1349 // check if ~/.cache is a valid destination
1350 struct stat s;
1351 if (is_link(cache)) {
1352 fwarning("~/.cache is a symbolic link, tmpfs not mounted\n");
1353 return;
1354 }
1355 if (stat(cache, &s) == -1 || !S_ISDIR(s.st_mode)) {
1356 fwarning("no ~/.cache directory found, tmpfs not mounted\n");
1357 return;
1358 }
1359 if (s.st_uid != getuid()) {
1360 fwarning("~/.cache is not owned by user, tmpfs not mounted\n");
1361 return;
1362 }
1363
1364 // get a file descriptor for ~/.cache, fails if there is any symlink
1365 int fd = safe_fd(cache, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1366 if (fd == -1)
1367 errExit("safe_fd");
1368 // confirm that actual mount destination is owned by the user
1369 if (fstat(fd, &s) == -1 || s.st_uid != getuid())
1370 errExit("fstat");
1371
1372 // mount a tmpfs on ~/.cache via the symbolic link in /proc/self/fd
1373 char *proc;
1374 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
1375 errExit("asprintf");
1376 if (mount("tmpfs", proc, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, 0) < 0)
1377 errExit("mounting tmpfs");
1378 fs_logger2("tmpfs", cache);
1379 free(proc);
1380 close(fd);
1381 // check the last mount operation
1382 MountData *mdata = get_last_mount();
1383 assert(mdata);
1384 if (strcmp(mdata->fstype, "tmpfs") != 0 || strcmp(mdata->dir, cache) != 0)
1385 errLogExit("invalid .cache mount");
1386
1387 // get a new file descriptor for ~/.cache, the old directory is masked by the tmpfs
1388 fd = safe_fd(cache, O_RDONLY|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1389 if (fd == -1)
1390 errExit("safe_fd");
1391 free(cache);
1392 // restore permissions
1393 SET_PERMS_FD(fd, s.st_uid, s.st_gid, s.st_mode);
1394 close(fd);
1395}
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 9d28f3352..7d4c33460 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -45,7 +45,8 @@ gid_t firejail_gid = 0;
45static char child_stack[STACK_SIZE]; // space for child's stack 45static char child_stack[STACK_SIZE]; // space for child's stack
46Config cfg; // configuration 46Config cfg; // configuration
47int arg_private = 0; // mount private /home and /tmp directoryu 47int arg_private = 0; // mount private /home and /tmp directoryu
48int arg_private_template = 0; // mount private /home using a template 48int arg_private_template = 0; // mount private /home using a template
49int arg_private_cache = 0; // mount private home/.cache
49int arg_debug = 0; // print debug messages 50int arg_debug = 0; // print debug messages
50int arg_debug_blacklists = 0; // print debug messages for blacklists 51int arg_debug_blacklists = 0; // print debug messages for blacklists
51int arg_debug_whitelists = 0; // print debug messages for whitelists 52int arg_debug_whitelists = 0; // print debug messages for whitelists
@@ -1676,6 +1677,9 @@ int main(int argc, char **argv) {
1676 else if (strcmp(argv[i], "--private-tmp") == 0) { 1677 else if (strcmp(argv[i], "--private-tmp") == 0) {
1677 arg_private_tmp = 1; 1678 arg_private_tmp = 1;
1678 } 1679 }
1680 else if (strcmp(argv[i], "--private-cache") == 0) {
1681 arg_private_cache = 1;
1682 }
1679 1683
1680 //************************************* 1684 //*************************************
1681 // hostname, etc 1685 // hostname, etc
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index 7b59cd48c..04519483c 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -217,6 +217,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
217 arg_allusers = 1; 217 arg_allusers = 1;
218 return 0; 218 return 0;
219 } 219 }
220 else if (strcmp(ptr, "private-cache") == 0) {
221 arg_private_cache = 1;
222 return 0;
223 }
220 else if (strcmp(ptr, "private-dev") == 0) { 224 else if (strcmp(ptr, "private-dev") == 0) {
221 arg_private_dev = 1; 225 arg_private_dev = 1;
222 return 0; 226 return 0;
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 1498007eb..fdb0babc8 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -833,6 +833,15 @@ int sandbox(void* sandbox_arg) {
833 } 833 }
834 } 834 }
835 835
836 if (arg_private_cache) {
837 if (cfg.chrootdir)
838 fwarning("private-cache feature is disabled in chroot\n");
839 else if (arg_overlay)
840 fwarning("private-cache feature is disabled in overlay\n");
841 else
842 fs_private_cache();
843 }
844
836 if (arg_private_tmp) { 845 if (arg_private_tmp) {
837 // private-tmp is implemented as a whitelist 846 // private-tmp is implemented as a whitelist
838 EUID_USER(); 847 EUID_USER();
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 8b3996927..f5a252514 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -1111,3 +1111,53 @@ errexit:
1111 fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n"); 1111 fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n");
1112 exit(1); 1112 exit(1);
1113} 1113}
1114
1115// The returned file descriptor should be suitable for privileged operations on
1116// user controlled paths. Passed flags are ignored if path is a top level directory.
1117int safe_fd(const char *path, int flags) {
1118 assert(path);
1119 int fd;
1120
1121 // work with a copy of path
1122 char *dup = strdup(path);
1123 if (dup == NULL)
1124 errExit("strdup");
1125 if (*dup != '/')
1126 errExit("relative path"); // or empty string
1127
1128 char *p = strrchr(dup, '/');
1129 if (p == NULL)
1130 errExit("strrchr");
1131 if (*(p + 1) == '\0')
1132 errExit("trailing slash"); // or root dir
1133
1134 int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC);
1135 if (parentfd == -1)
1136 errExit("open");
1137
1138 // if there is more than one path segment, keep the last one for later
1139 if (p != dup)
1140 *p = '\0';
1141
1142 // traverse the path, return -1 if a symlink is encountered
1143 char *tok = strtok(dup, "/");
1144 if (tok == NULL)
1145 errExit("strtok");
1146 while (tok) {
1147 fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1148 close(parentfd);
1149 if (fd == -1) {
1150 free(dup);
1151 return -1;
1152 }
1153 parentfd = fd;
1154 tok = strtok(NULL, "/");
1155 }
1156 if (p != dup) {
1157 // open last path segment
1158 fd = openat(parentfd, p + 1, flags|O_NOFOLLOW);
1159 close(parentfd);
1160 }
1161 free(dup);
1162 return fd; // -1 if open failed
1163}