aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2018-08-02 21:23:50 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2018-08-02 21:23:50 +0200
commita920087f837dcf236acf9bc7a63494c34d72cc93 (patch)
tree9313a646b93a254fb2fefe43a88c486301749436 /src
parentMerge branch 'master' of https://github.com/netblue30/firejail (diff)
downloadfirejail-a920087f837dcf236acf9bc7a63494c34d72cc93.tar.gz
firejail-a920087f837dcf236acf9bc7a63494c34d72cc93.tar.zst
firejail-a920087f837dcf236acf9bc7a63494c34d72cc93.zip
port whitelist path creation to "at" family of functions
Diffstat (limited to 'src')
-rw-r--r--src/firejail/fs_home.c2
-rw-r--r--src/firejail/fs_whitelist.c197
2 files changed, 131 insertions, 68 deletions
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 3afa3bf0c..09931bd56 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -290,6 +290,8 @@ void fs_private(void) {
290 if (u == 0 && arg_allusers) // allow --allusers when starting the sandbox as root 290 if (u == 0 && arg_allusers) // allow --allusers when starting the sandbox as root
291 ; 291 ;
292 else { 292 else {
293 if (arg_allusers)
294 fwarning("--allusers disabled by --private or --whitelist\n");
293 if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) 295 if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
294 errExit("mounting home directory"); 296 errExit("mounting home directory");
295 fs_logger("tmpfs /home"); 297 fs_logger("tmpfs /home");
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 0178e3c5b..df0c21e12 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -51,46 +51,75 @@ char *parse_nowhitelist(int nowhitelist_flag, char *ptr1) {
51 51
52static int mkpath(const char* path, mode_t mode) { 52static int mkpath(const char* path, mode_t mode) {
53 assert(path && *path); 53 assert(path && *path);
54
55 mode |= 0111; 54 mode |= 0111;
56 55
57 // create directories with uid/gid as root or as current user if inside home directory 56 // create directories with uid/gid as root or as current user if inside home directory
58 uid_t uid = getuid(); 57 if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) == 0) {
59 gid_t gid = getgid(); 58 EUID_USER();
60 if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) != 0) {
61 uid = 0;
62 gid = 0;
63 } 59 }
64 60
65 // work on a copy of the path 61 // work on a copy of the path
66 char *file_path = strdup(path); 62 char *dup = strdup(path);
67 if (!file_path) 63 if (!dup)
68 errExit("strdup"); 64 errExit("strdup");
69 65
70 char* p; 66 // don't create the last path element
67 char *p = strrchr(dup, '/');
68 if (!p)
69 errExit("strrchr");
70 *p = '\0';
71
72 int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC);
73 if (parentfd == -1)
74 errExit("open");
75
76 // traverse the path, return -1 if a symlink is encountered
71 int done = 0; 77 int done = 0;
72 for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) { 78 int fd = -1;
73 *p='\0'; 79 char *tok = strtok(dup, "/");
74 if (mkdir(file_path, mode)==-1) { 80 if (!tok)
81 errExit("strtok");
82 while (tok) {
83 // skip all instances of "/./"
84 if (strcmp(tok, ".") == 0) {
85 tok = strtok(NULL, "/");
86 continue;
87 }
88 // create the directory if necessary
89 if (mkdirat(parentfd, tok, mode) == -1) {
75 if (errno != EEXIST) { 90 if (errno != EEXIST) {
76 *p='/'; 91 if (arg_debug || arg_debug_whitelists)
77 free(file_path); 92 perror("mkdir");
93 close(parentfd);
94 free(dup);
95 EUID_ROOT();
78 return -1; 96 return -1;
79 } 97 }
80 } 98 }
81 else { 99 else
82 if (set_perms(file_path, uid, gid, mode))
83 errExit("set_perms");
84 done = 1; 100 done = 1;
101 // open the directory
102 fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
103 if (fd == -1) {
104 if (arg_debug || arg_debug_whitelists)
105 perror("open");
106 close(parentfd);
107 free(dup);
108 EUID_ROOT();
109 return -1;
85 } 110 }
86 111 // move on to next path segment
87 *p='/'; 112 close(parentfd);
113 parentfd = fd;
114 tok = strtok(NULL, "/");
88 } 115 }
116
89 if (done) 117 if (done)
90 fs_logger2("mkpath", path); 118 fs_logger2("mkpath", path);
91 119
92 free(file_path); 120 free(dup);
93 return 0; 121 EUID_ROOT();
122 return fd;
94} 123}
95 124
96static void whitelist_path(ProfileEntry *entry) { 125static void whitelist_path(ProfileEntry *entry) {
@@ -184,13 +213,18 @@ static void whitelist_path(ProfileEntry *entry) {
184 } 213 }
185 assert(wfile); 214 assert(wfile);
186 215
187 // check if the file exists, confirm again there is no symlink 216 if (arg_debug || arg_debug_whitelists)
217 printf("Whitelisting %s\n", path);
218
219 // confirm again the mount source exists and there is no symlink
188 struct stat wfilestat; 220 struct stat wfilestat;
189#ifndef TEST_MOUNTINFO 221#ifndef TEST_MOUNTINFO
190 EUID_USER(); 222 EUID_USER();
191 int fd = safe_fd(wfile, O_PATH|O_NOFOLLOW|O_CLOEXEC); 223 int fd = safe_fd(wfile, O_PATH|O_NOFOLLOW|O_CLOEXEC);
192 EUID_ROOT(); 224 EUID_ROOT();
193 if (fd == -1) { 225 if (fd == -1) {
226 if (arg_debug || arg_debug_whitelists)
227 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path);
194 free(wfile); 228 free(wfile);
195 return; 229 return;
196 } 230 }
@@ -198,57 +232,64 @@ static void whitelist_path(ProfileEntry *entry) {
198 errExit("fstat"); 232 errExit("fstat");
199 close(fd); 233 close(fd);
200 if (S_ISLNK(wfilestat.st_mode)) { 234 if (S_ISLNK(wfilestat.st_mode)) {
235 if (arg_debug || arg_debug_whitelists)
236 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path);
201 free(wfile); 237 free(wfile);
202 return; 238 return;
203 } 239 }
204#endif 240#endif
205 241
206 if (arg_debug || arg_debug_whitelists) 242 // create path of the mount target if necessary
207 printf("Whitelisting %s\n", path); 243 int fd2 = mkpath(path, 0755);
244 if (fd2 == -1) {
245 // something went wrong during path creation or a symlink was found;
246 // if there is a symlink somewhere in the path of the mount target,
247 // assume the file is whitelisted already
248 if (arg_debug || arg_debug_whitelists)
249 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path);
250 free(wfile);
251 return;
252 }
208 253
209 // create the path if necessary 254 // get file name of the mount target
210 struct stat s; 255 const char *file = gnu_basename(path);
211 if (stat(path, &s) == -1) { 256
212 mkpath(path, 0755); 257 // create the mount target if necessary and open it, a symlink is rejected
213 if (S_ISDIR(wfilestat.st_mode)) { 258 int fd3 = -1;
214 int rv = mkdir(path, 0755); 259 if (S_ISDIR(wfilestat.st_mode)) {
215 if (rv) { 260 // directory foo can exist already:
216 fprintf(stderr, "Error: cannot create directory %s\n", path); 261 // firejail --whitelist=/foo/bar --whitelist=/foo
217 exit(1); 262 if (mkdirat(fd2, file, 0755) == -1 && errno != EEXIST) {
218 } 263 if (arg_debug || arg_debug_whitelists) {
219 } 264 perror("mkdir");
220 else { 265 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path);
221 int fd2 = open(path, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR);
222 if (fd2 == -1) {
223 fprintf(stderr, "Error: cannot create empty file %s\n", path);
224 exit(1);
225 } 266 }
226 close(fd2); 267 close(fd2);
268 free(wfile);
269 return;
227 } 270 }
271 fd3 = openat(fd2, file, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
228 } 272 }
229 else { 273 else {
230 if (!S_ISDIR(s.st_mode)) { 274 // create an empty file, fails with EEXIST if it is whitelisted already:
231 free(wfile); 275 // firejail --whitelist=/foo --whitelist=/foo/bar
232 return; // the file is already present 276 fd3 = openat(fd2, file, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR);
233 }
234 } 277 }
235 278
236 fs_logger2("whitelist", path);
237
238 // get a file descriptor for path; if path contains anything other than directories
239 // or a regular file, assume it is whitelisted already
240 int fd3 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
241 if (fd3 == -1) { 279 if (fd3 == -1) {
280 if (arg_debug || arg_debug_whitelists) {
281 if (errno != EEXIST) {
282 perror("open");
283 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path);
284 }
285 }
286 close(fd2);
242 free(wfile); 287 free(wfile);
243 return; 288 return;
244 } 289 }
245 if (fstat(fd3, &s) == -1) 290 close(fd2);
246 errExit("fstat"); 291
247 if (!(S_ISDIR(s.st_mode) || S_ISREG(s.st_mode))) { 292 fs_logger2("whitelist", path);
248 free(wfile);
249 close(fd3);
250 return;
251 }
252 293
253 // mount via the link in /proc/self/fd 294 // mount via the link in /proc/self/fd
254 char *proc; 295 char *proc;
@@ -272,6 +313,7 @@ static void whitelist_path(ProfileEntry *entry) {
272 int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 313 int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
273 if (fd4 == -1) 314 if (fd4 == -1)
274 errExit("safe_fd"); 315 errExit("safe_fd");
316 struct stat s;
275 if (fstat(fd4, &s) == -1) 317 if (fstat(fd4, &s) == -1)
276 errExit("fstat"); 318 errExit("fstat");
277 if (s.st_dev != wfilestat.st_dev || s.st_ino != wfilestat.st_ino) 319 if (s.st_dev != wfilestat.st_dev || s.st_ino != wfilestat.st_ino)
@@ -501,6 +543,14 @@ void fs_whitelist(void) {
501 goto errexit; 543 goto errexit;
502 } 544 }
503 545
546 // no trailing slash and no trailing dot
547 char *p = strrchr(new_name, '/');
548 if (!p)
549 errExit("strrchr");
550 if (*(p + 1) == '\0')
551 goto errexit;
552 if (*(p + 1) == '.' && *(p + 2) == '\0')
553 goto errexit;
504 554
505 // extract the absolute path of the file 555 // extract the absolute path of the file
506 // realpath function will fail with ENOENT if the file is not found 556 // realpath function will fail with ENOENT if the file is not found
@@ -577,7 +627,6 @@ void fs_whitelist(void) {
577 continue; 627 continue;
578 } 628 }
579 629
580
581 // check for supported directories 630 // check for supported directories
582 if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { 631 if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) {
583 // whitelisting home directory is disabled if --private option is present 632 // whitelisting home directory is disabled if --private option is present
@@ -597,7 +646,7 @@ void fs_whitelist(void) {
597 646
598 // both path and absolute path are under /home 647 // both path and absolute path are under /home
599 if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0) { 648 if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0) {
600 // avoid naming issues, also entire home dirs are not allowed 649 // entire home directory is not allowed
601 if (*(fname + strlen(cfg.homedir)) != '/') 650 if (*(fname + strlen(cfg.homedir)) != '/')
602 goto errexit; 651 goto errexit;
603 } 652 }
@@ -744,7 +793,7 @@ void fs_whitelist(void) {
744 entry->link = new_name; 793 entry->link = new_name;
745 else { 794 else {
746 free(new_name); 795 free(new_name);
747 new_name = NULL; 796 entry->link = NULL;
748 } 797 }
749 798
750 // change file name in entry->data 799 // change file name in entry->data
@@ -774,7 +823,7 @@ void fs_whitelist(void) {
774 if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) 823 if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
775 errExit("mount bind"); 824 errExit("mount bind");
776 825
777 // mount a tmpfs and initialize /home/user 826 // mount a tmpfs and initialize /home/user, overrides --allusers
778 fs_private(); 827 fs_private();
779 } 828 }
780 else 829 else
@@ -986,14 +1035,29 @@ void fs_whitelist(void) {
986 // if the link is already there, do not bother 1035 // if the link is already there, do not bother
987 if (lstat(entry->link, &s) != 0) { 1036 if (lstat(entry->link, &s) != 0) {
988 // create the path if necessary 1037 // create the path if necessary
989 mkpath(entry->link, 0755); 1038 int fd = mkpath(entry->link, 0755);
990 1039 if (fd == -1) {
991 int rv = symlink(entry->data + 10, entry->link); 1040 if (arg_debug || arg_debug_whitelists)
992 if (rv) 1041 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link);
993 fprintf(stderr, "Warning cannot create symbolic link %s\n", entry->link); 1042 free(entry->link);
1043 entry = entry->next;
1044 continue;
1045 }
1046 // get file name of symlink
1047 const char *file = gnu_basename(entry->link);
1048 // create the link
1049 int rv = symlinkat(entry->data + 10, fd, file);
1050 if (rv) {
1051 if (arg_debug || arg_debug_whitelists) {
1052 perror("symlink");
1053 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link);
1054 }
1055 }
994 else if (arg_debug || arg_debug_whitelists) 1056 else if (arg_debug || arg_debug_whitelists)
995 printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10); 1057 printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10);
1058 close(fd);
996 } 1059 }
1060 free(entry->link);
997 } 1061 }
998 1062
999 entry = entry->next; 1063 entry = entry->next;
@@ -1076,9 +1140,6 @@ void fs_whitelist(void) {
1076 fs_logger2("tmpfs", RUN_WHITELIST_MODULE_DIR); 1140 fs_logger2("tmpfs", RUN_WHITELIST_MODULE_DIR);
1077 } 1141 }
1078 1142
1079 if (new_name)
1080 free(new_name);
1081
1082 return; 1143 return;
1083 1144
1084errexit: 1145errexit: