aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2022-02-25 22:36:34 +0100
committerLibravatar smitsohu <smitsohu@gmail.com>2022-03-01 01:37:45 +0100
commitf60dbcda4d3409c1deb79bcb874a43173970401a (patch)
tree2f75c6ad84a236e3c4e258872de4ab69e988ed4f /src
parentmount id: drop effective user id assertions (diff)
downloadfirejail-f60dbcda4d3409c1deb79bcb874a43173970401a.tar.gz
firejail-f60dbcda4d3409c1deb79bcb874a43173970401a.tar.zst
firejail-f60dbcda4d3409c1deb79bcb874a43173970401a.zip
whitelist: minor restructuring
some cleanup, simplify extending the code (for example adding additional members to the TopDir struct)
Diffstat (limited to 'src')
-rw-r--r--src/firejail/fs_whitelist.c125
1 files changed, 65 insertions, 60 deletions
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 2acde5837..0b860c0d5 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -49,21 +49,25 @@ static void whitelist_error(const char *path) {
49 exit(1); 49 exit(1);
50} 50}
51 51
52static int whitelist_mkpath(const char* path, mode_t mode) { 52static int whitelist_mkpath(const char *parentdir, const char *relpath, mode_t mode) {
53 // starting from top level directory
54 int parentfd = safer_openat(-1, parentdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
55 if (parentfd < 0)
56 errExit("open");
57
53 // work on a copy of the path 58 // work on a copy of the path
54 char *dup = strdup(path); 59 char *dup = strdup(relpath);
55 if (!dup) 60 if (!dup)
56 errExit("strdup"); 61 errExit("strdup");
57 62
58 // only create leading directories, don't create the file 63 // only create leading directories, don't create the file
59 char *p = strrchr(dup, '/'); 64 char *p = strrchr(dup, '/');
60 assert(p); 65 if (!p) { // nothing to do
66 free(dup);
67 return parentfd;
68 }
61 *p = '\0'; 69 *p = '\0';
62 70
63 int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC);
64 if (parentfd == -1)
65 errExit("open");
66
67 // traverse the path, return -1 if a symlink is encountered 71 // traverse the path, return -1 if a symlink is encountered
68 int fd = -1; 72 int fd = -1;
69 int done = 0; 73 int done = 0;
@@ -97,24 +101,35 @@ static int whitelist_mkpath(const char* path, mode_t mode) {
97 tok = strtok(NULL, "/"); 101 tok = strtok(NULL, "/");
98 } 102 }
99 103
100 if (done) 104 if (done) {
101 fs_logger2("mkpath", path); 105 char *abspath;
106 if (asprintf(&abspath, "%s/%s", parentdir, relpath) < 0)
107 errExit("asprintf");
108 fs_logger2("mkpath", abspath);
109 free(abspath);
110 }
102 111
103 free(dup); 112 free(dup);
104 return fd; 113 return fd;
105} 114}
106 115
107static void whitelist_file(int dirfd, const char *relpath, const char *path) { 116static void whitelist_file(const TopDir * const top, const char *path) {
108 EUID_ASSERT(); 117 EUID_ASSERT();
109 assert(relpath && path); 118 assert(top && path);
119
120 // check if path is inside top level directory
121 size_t top_pathlen = strlen(top->path);
122 if (strncmp(top->path, path, top_pathlen) != 0 || path[top_pathlen] != '/')
123 return;
124 const char *relpath = path + top_pathlen + 1;
110 125
111 // open mount source, using a file descriptor that refers to the 126 // open mount source, using a file descriptor that refers to the
112 // top level directory 127 // top level directory
113 // as the top level directory was opened before mounting the tmpfs 128 // as the top level directory was opened before mounting the tmpfs
114 // we still have full access to all directory contents 129 // we still have full access to all directory contents
115 // take care to not follow symbolic links (dirfd was obtained without 130 // take care to not follow symbolic links (top->fd was obtained without
116 // following a link, too) 131 // following a link, too)
117 int fd = safer_openat(dirfd, relpath, O_PATH|O_NOFOLLOW|O_CLOEXEC); 132 int fd = safer_openat(top->fd, relpath, O_PATH|O_NOFOLLOW|O_CLOEXEC);
118 if (fd == -1) { 133 if (fd == -1) {
119 if (arg_debug || arg_debug_whitelists) 134 if (arg_debug || arg_debug_whitelists)
120 printf("Debug %d: skip whitelist %s\n", __LINE__, path); 135 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
@@ -130,17 +145,15 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
130 return; 145 return;
131 } 146 }
132 147
148 // now modify the tmpfs:
133 // create mount target as root, except if inside home or run/user/$UID directory 149 // create mount target as root, except if inside home or run/user/$UID directory
134 if ((strncmp(path, cfg.homedir, homedir_len) != 0 || path[homedir_len] != '/') && 150 if (strcmp(top->path, cfg.homedir) != 0 &&
135 (strncmp(path, runuser, runuser_len) != 0 || path[runuser_len] != '/')) 151 strcmp(top->path, runuser) != 0)
136 EUID_ROOT(); 152 EUID_ROOT();
137 153
138 // create path of the mount target 154 // create path of the mount target
139 int fd2 = whitelist_mkpath(path, 0755); 155 int fd2 = whitelist_mkpath(top->path, relpath, 0755);
140 if (fd2 == -1) { 156 if (fd2 == -1) {
141 // something went wrong during path creation or a symlink was found;
142 // if there is a symlink somewhere in the path of the mount target,
143 // assume the file is whitelisted already
144 if (arg_debug || arg_debug_whitelists) 157 if (arg_debug || arg_debug_whitelists)
145 printf("Debug %d: skip whitelist %s\n", __LINE__, path); 158 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
146 close(fd); 159 close(fd);
@@ -149,13 +162,14 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
149 } 162 }
150 163
151 // get file name of the mount target 164 // get file name of the mount target
152 const char *file = gnu_basename(path); 165 const char *file = gnu_basename(relpath);
153 166
154 // create mount target itself and open it, a symlink is rejected 167 // create mount target itself if necessary
168 // and open it, a symlink is not allowed
155 int fd3 = -1; 169 int fd3 = -1;
156 if (S_ISDIR(s.st_mode)) { 170 if (S_ISDIR(s.st_mode)) {
157 // directory foo can exist already: 171 // directory bar can exist already:
158 // firejail --whitelist=~/foo/bar --whitelist=~/foo 172 // firejail --whitelist=/foo/bar/baz --whitelist=/foo/bar
159 if (mkdirat(fd2, file, 0755) == -1 && errno != EEXIST) { 173 if (mkdirat(fd2, file, 0755) == -1 && errno != EEXIST) {
160 if (arg_debug || arg_debug_whitelists) { 174 if (arg_debug || arg_debug_whitelists) {
161 perror("mkdir"); 175 perror("mkdir");
@@ -169,8 +183,8 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
169 fd3 = openat(fd2, file, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 183 fd3 = openat(fd2, file, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
170 } 184 }
171 else 185 else
172 // create an empty file, fails with EEXIST if it is whitelisted already: 186 // create an empty file
173 // firejail --whitelist=/foo --whitelist=/foo/bar 187 // fails with EEXIST if it is whitelisted already
174 fd3 = openat(fd2, file, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR); 188 fd3 = openat(fd2, file, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR);
175 189
176 if (fd3 == -1) { 190 if (fd3 == -1) {
@@ -212,16 +226,23 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
212 fs_logger2("whitelist", path); 226 fs_logger2("whitelist", path);
213} 227}
214 228
215static void whitelist_symlink(const char *link, const char *target) { 229static void whitelist_symlink(const TopDir * const top, const char *link, const char *target) {
216 EUID_ASSERT(); 230 EUID_ASSERT();
217 assert(link && target); 231 assert(top && link && target);
232
233 // confirm link is inside top level directory
234 // this should never fail
235 size_t top_pathlen = strlen(top->path);
236 assert(strncmp(top->path, link, top_pathlen) == 0 && link[top_pathlen] == '/');
237
238 const char *relpath = link + top_pathlen + 1;
218 239
219 // create files as root, except if inside home or run/user/$UID directory 240 // create link as root, except if inside home or run/user/$UID directory
220 if ((strncmp(link, cfg.homedir, homedir_len) != 0 || link[homedir_len] != '/') && 241 if (strcmp(top->path, cfg.homedir) != 0 &&
221 (strncmp(link, runuser, runuser_len) != 0 || link[runuser_len] != '/')) 242 strcmp(top->path, runuser) != 0)
222 EUID_ROOT(); 243 EUID_ROOT();
223 244
224 int fd = whitelist_mkpath(link, 0755); 245 int fd = whitelist_mkpath(top->path, relpath, 0755);
225 if (fd == -1) { 246 if (fd == -1) {
226 if (arg_debug || arg_debug_whitelists) 247 if (arg_debug || arg_debug_whitelists)
227 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, link); 248 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, link);
@@ -230,7 +251,7 @@ static void whitelist_symlink(const char *link, const char *target) {
230 } 251 }
231 252
232 // get file name of symlink 253 // get file name of symlink
233 const char *file = gnu_basename(link); 254 const char *file = gnu_basename(relpath);
234 255
235 // create the link 256 // create the link
236 if (symlinkat(target, fd, file) == -1) { 257 if (symlinkat(target, fd, file) == -1) {
@@ -285,7 +306,7 @@ static void globbing(const char *pattern) {
285} 306}
286 307
287// mount tmpfs on all top level directories 308// mount tmpfs on all top level directories
288static void tmpfs_topdirs(const TopDir *topdirs) { 309static void tmpfs_topdirs(const TopDir * const topdirs) {
289 int tmpfs_home = 0; 310 int tmpfs_home = 0;
290 int tmpfs_runuser = 0; 311 int tmpfs_runuser = 0;
291 312
@@ -329,9 +350,7 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
329 fs_logger2("whitelist", RUN_FIREJAIL_DIR); 350 fs_logger2("whitelist", RUN_FIREJAIL_DIR);
330 351
331 // restore /run/user/$UID directory 352 // restore /run/user/$UID directory
332 // get path relative to /run 353 whitelist_file(&topdirs[i], runuser);
333 const char *rel = runuser + 5;
334 whitelist_file(topdirs[i].fd, rel, runuser);
335 } 354 }
336 else if (strcmp(topdirs[i].path, "/tmp") == 0) { 355 else if (strcmp(topdirs[i].path, "/tmp") == 0) {
337 // fix pam-tmpdir (#2685) 356 // fix pam-tmpdir (#2685)
@@ -371,12 +390,7 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
371 // restore user home directory if it is masked by the tmpfs 390 // restore user home directory if it is masked by the tmpfs
372 // creates path owned by root 391 // creates path owned by root
373 // does nothing if user home directory doesn't exist 392 // does nothing if user home directory doesn't exist
374 size_t topdir_len = strlen(topdirs[i].path); 393 whitelist_file(&topdirs[i], cfg.homedir);
375 if (strncmp(topdirs[i].path, cfg.homedir, topdir_len) == 0 && cfg.homedir[topdir_len] == '/') {
376 // get path relative to top level directory
377 const char *rel = cfg.homedir + topdir_len + 1;
378 whitelist_file(topdirs[i].fd, rel, cfg.homedir);
379 }
380 } 394 }
381 395
382 // user home directory 396 // user home directory
@@ -720,9 +734,6 @@ void fs_whitelist(void) {
720 entry = entry->next; 734 entry = entry->next;
721 } 735 }
722 736
723 // release nowhitelist memory
724 free(nowhitelist);
725
726 // mount tmpfs on all top level directories 737 // mount tmpfs on all top level directories
727 tmpfs_topdirs(topdirs); 738 tmpfs_topdirs(topdirs);
728 739
@@ -732,24 +743,15 @@ void fs_whitelist(void) {
732 if (entry->wparam) { 743 if (entry->wparam) {
733 char *file = entry->wparam->file; 744 char *file = entry->wparam->file;
734 char *link = entry->wparam->link; 745 char *link = entry->wparam->link;
735 const char *topdir = entry->wparam->top->path; 746 const TopDir * const current_top = entry->wparam->top;
736 size_t topdir_len = strlen(topdir);
737 int dirfd = entry->wparam->top->fd;
738 747
739 // top level directories of link and file can differ 748 // top level directories of link and file can differ
740 // whitelist the file only if it is in same top level directory 749 // will whitelist the file only if it is in same top level directory
741 if (strncmp(file, topdir, topdir_len) == 0 && file[topdir_len] == '/') { 750 whitelist_file(current_top, file);
742 // get path relative to top level directory
743 const char *rel = file + topdir_len + 1;
744
745 if (arg_debug || arg_debug_whitelists)
746 printf("Debug %d: file: %s; dirfd: %d; topdir: %s; rel: %s\n", __LINE__, file, dirfd, topdir, rel);
747 whitelist_file(dirfd, rel, file);
748 }
749 751
750 // create the link if any 752 // create the link if any
751 if (link) { 753 if (link) {
752 whitelist_symlink(link, file); 754 whitelist_symlink(current_top, link, file);
753 free(link); 755 free(link);
754 } 756 }
755 757
@@ -762,12 +764,15 @@ void fs_whitelist(void) {
762 } 764 }
763 765
764 // release resources 766 // release resources
765 free(runuser);
766
767 size_t i; 767 size_t i;
768 for (i = 0; i < nowhitelist_c; i++)
769 free(nowhitelist[i]);
770 free(nowhitelist);
771
768 for (i = 0; i < TOP_MAX && topdirs[i].path; i++) { 772 for (i = 0; i < TOP_MAX && topdirs[i].path; i++) {
769 free(topdirs[i].path); 773 free(topdirs[i].path);
770 close(topdirs[i].fd); 774 close(topdirs[i].fd);
771 } 775 }
772 free(topdirs); 776 free(topdirs);
777 free(runuser);
773} 778}