diff options
Diffstat (limited to 'src/firejail/fs_whitelist.c')
-rw-r--r-- | src/firejail/fs_whitelist.c | 235 |
1 files changed, 152 insertions, 83 deletions
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index a950d1124..666f02e4d 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -46,9 +46,10 @@ static int mkpath(const char* path, mode_t mode) { | |||
46 | assert(path && *path); | 46 | assert(path && *path); |
47 | mode |= 0111; | 47 | mode |= 0111; |
48 | 48 | ||
49 | // create directories with uid/gid as root or as current user if inside home or run directory | 49 | // create directories with uid/gid as root, or as current user if inside home or run/user/$uid directory |
50 | int userprivs = 0; | 50 | int userprivs = 0; |
51 | if (strncmp(path, cfg.homedir, homedir_len) == 0 || strncmp(path, runuser, runuser_len) == 0) { | 51 | if ((strncmp(path, cfg.homedir, homedir_len) == 0 && path[homedir_len] == '/') || |
52 | (strncmp(path, runuser, runuser_len) == 0 && path[runuser_len] == '/')) { | ||
52 | EUID_USER(); | 53 | EUID_USER(); |
53 | userprivs = 1; | 54 | userprivs = 1; |
54 | } | 55 | } |
@@ -122,7 +123,7 @@ static void whitelist_path(ProfileEntry *entry) { | |||
122 | const char *fname; | 123 | const char *fname; |
123 | char *wfile = NULL; | 124 | char *wfile = NULL; |
124 | 125 | ||
125 | if (entry->home_dir) { | 126 | if (entry->wldir == WLDIR_HOME) { |
126 | if (strncmp(path, cfg.homedir, homedir_len) != 0 || path[homedir_len] != '/') | 127 | if (strncmp(path, cfg.homedir, homedir_len) != 0 || path[homedir_len] != '/') |
127 | // either symlink pointing outside home directory | 128 | // either symlink pointing outside home directory |
128 | // or entire home directory, skip the mount | 129 | // or entire home directory, skip the mount |
@@ -133,25 +134,25 @@ static void whitelist_path(ProfileEntry *entry) { | |||
133 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1) | 134 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1) |
134 | errExit("asprintf"); | 135 | errExit("asprintf"); |
135 | } | 136 | } |
136 | else if (entry->tmp_dir) { | 137 | else if (entry->wldir == WLDIR_TMP) { |
137 | fname = path + 5; // strlen("/tmp/") | 138 | fname = path + 5; // strlen("/tmp/") |
138 | 139 | ||
139 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_TMP_DIR, fname) == -1) | 140 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_TMP_DIR, fname) == -1) |
140 | errExit("asprintf"); | 141 | errExit("asprintf"); |
141 | } | 142 | } |
142 | else if (entry->media_dir) { | 143 | else if (entry->wldir == WLDIR_MEDIA) { |
143 | fname = path + 7; // strlen("/media/") | 144 | fname = path + 7; // strlen("/media/") |
144 | 145 | ||
145 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MEDIA_DIR, fname) == -1) | 146 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MEDIA_DIR, fname) == -1) |
146 | errExit("asprintf"); | 147 | errExit("asprintf"); |
147 | } | 148 | } |
148 | else if (entry->mnt_dir) { | 149 | else if (entry->wldir == WLDIR_MNT) { |
149 | fname = path + 5; // strlen("/mnt/") | 150 | fname = path + 5; // strlen("/mnt/") |
150 | 151 | ||
151 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MNT_DIR, fname) == -1) | 152 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MNT_DIR, fname) == -1) |
152 | errExit("asprintf"); | 153 | errExit("asprintf"); |
153 | } | 154 | } |
154 | else if (entry->var_dir) { | 155 | else if (entry->wldir == WLDIR_VAR) { |
155 | if (strncmp(path, "/var/", 5) != 0) | 156 | if (strncmp(path, "/var/", 5) != 0) |
156 | // symlink pointing outside /var, skip the mount | 157 | // symlink pointing outside /var, skip the mount |
157 | return; | 158 | return; |
@@ -161,7 +162,7 @@ static void whitelist_path(ProfileEntry *entry) { | |||
161 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_VAR_DIR, fname) == -1) | 162 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_VAR_DIR, fname) == -1) |
162 | errExit("asprintf"); | 163 | errExit("asprintf"); |
163 | } | 164 | } |
164 | else if (entry->dev_dir) { | 165 | else if (entry->wldir == WLDIR_DEV) { |
165 | if (strncmp(path, "/dev/", 5) != 0) | 166 | if (strncmp(path, "/dev/", 5) != 0) |
166 | // symlink pointing outside /dev, skip the mount | 167 | // symlink pointing outside /dev, skip the mount |
167 | return; | 168 | return; |
@@ -171,19 +172,19 @@ static void whitelist_path(ProfileEntry *entry) { | |||
171 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_DEV_DIR, fname) == -1) | 172 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_DEV_DIR, fname) == -1) |
172 | errExit("asprintf"); | 173 | errExit("asprintf"); |
173 | } | 174 | } |
174 | else if (entry->opt_dir) { | 175 | else if (entry->wldir == WLDIR_OPT) { |
175 | fname = path + 5; // strlen("/opt/") | 176 | fname = path + 5; // strlen("/opt/") |
176 | 177 | ||
177 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_OPT_DIR, fname) == -1) | 178 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_OPT_DIR, fname) == -1) |
178 | errExit("asprintf"); | 179 | errExit("asprintf"); |
179 | } | 180 | } |
180 | else if (entry->srv_dir) { | 181 | else if (entry->wldir == WLDIR_SRV) { |
181 | fname = path + 5; // strlen("/srv/") | 182 | fname = path + 5; // strlen("/srv/") |
182 | 183 | ||
183 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SRV_DIR, fname) == -1) | 184 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SRV_DIR, fname) == -1) |
184 | errExit("asprintf"); | 185 | errExit("asprintf"); |
185 | } | 186 | } |
186 | else if (entry->etc_dir) { | 187 | else if (entry->wldir == WLDIR_ETC) { |
187 | if (strncmp(path, "/etc/", 5) != 0) | 188 | if (strncmp(path, "/etc/", 5) != 0) |
188 | // symlink pointing outside /etc, skip the mount | 189 | // symlink pointing outside /etc, skip the mount |
189 | return; | 190 | return; |
@@ -193,19 +194,19 @@ static void whitelist_path(ProfileEntry *entry) { | |||
193 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_ETC_DIR, fname) == -1) | 194 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_ETC_DIR, fname) == -1) |
194 | errExit("asprintf"); | 195 | errExit("asprintf"); |
195 | } | 196 | } |
196 | else if (entry->share_dir) { | 197 | else if (entry->wldir == WLDIR_SHARE) { |
197 | fname = path + 11; // strlen("/usr/share/") | 198 | fname = path + 11; // strlen("/usr/share/") |
198 | 199 | ||
199 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SHARE_DIR, fname) == -1) | 200 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SHARE_DIR, fname) == -1) |
200 | errExit("asprintf"); | 201 | errExit("asprintf"); |
201 | } | 202 | } |
202 | else if (entry->module_dir) { | 203 | else if (entry->wldir == WLDIR_MODULE) { |
203 | fname = path + 12; // strlen("/sys/module/") | 204 | fname = path + 12; // strlen("/sys/module/") |
204 | 205 | ||
205 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MODULE_DIR, fname) == -1) | 206 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MODULE_DIR, fname) == -1) |
206 | errExit("asprintf"); | 207 | errExit("asprintf"); |
207 | } | 208 | } |
208 | else if (entry->run_dir) { | 209 | else if (entry->wldir == WLDIR_RUN) { |
209 | fname = path + runuser_len + 1; // strlen("/run/user/$uid/") | 210 | fname = path + runuser_len + 1; // strlen("/run/user/$uid/") |
210 | 211 | ||
211 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_RUN_USER_DIR, fname) == -1) | 212 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_RUN_USER_DIR, fname) == -1) |
@@ -329,6 +330,20 @@ static void whitelist_path(ProfileEntry *entry) { | |||
329 | return; | 330 | return; |
330 | } | 331 | } |
331 | 332 | ||
333 | static void whitelist_home(int topdir) { | ||
334 | ProfileEntry entry; | ||
335 | memset(&entry, 0, sizeof(entry)); | ||
336 | char *cmd; | ||
337 | if (asprintf(&cmd, "whitelist %s", cfg.homedir) == -1) | ||
338 | errExit("asprintf"); | ||
339 | entry.data = cmd; | ||
340 | entry.wldir = topdir; | ||
341 | // creates path owned by root, except homedir is inside /run/user/$uid | ||
342 | // does nothing if homedir does not exist | ||
343 | whitelist_path(&entry); | ||
344 | free(cmd); | ||
345 | } | ||
346 | |||
332 | 347 | ||
333 | void fs_whitelist(void) { | 348 | void fs_whitelist(void) { |
334 | ProfileEntry *entry = cfg.profile; | 349 | ProfileEntry *entry = cfg.profile; |
@@ -508,7 +523,7 @@ void fs_whitelist(void) { | |||
508 | continue; | 523 | continue; |
509 | } | 524 | } |
510 | 525 | ||
511 | entry->home_dir = 1; | 526 | entry->wldir = WLDIR_HOME; |
512 | home_dir = 1; | 527 | home_dir = 1; |
513 | if (arg_debug || arg_debug_whitelists) | 528 | if (arg_debug || arg_debug_whitelists) |
514 | fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n", | 529 | fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n", |
@@ -526,7 +541,7 @@ void fs_whitelist(void) { | |||
526 | } | 541 | } |
527 | } | 542 | } |
528 | else if (strncmp(new_name, "/tmp/", 5) == 0) { | 543 | else if (strncmp(new_name, "/tmp/", 5) == 0) { |
529 | entry->tmp_dir = 1; | 544 | entry->wldir = WLDIR_TMP; |
530 | tmp_dir = 1; | 545 | tmp_dir = 1; |
531 | 546 | ||
532 | // both path and absolute path are under /tmp | 547 | // both path and absolute path are under /tmp |
@@ -536,7 +551,7 @@ void fs_whitelist(void) { | |||
536 | } | 551 | } |
537 | } | 552 | } |
538 | else if (strncmp(new_name, "/media/", 7) == 0) { | 553 | else if (strncmp(new_name, "/media/", 7) == 0) { |
539 | entry->media_dir = 1; | 554 | entry->wldir = WLDIR_MEDIA; |
540 | media_dir = 1; | 555 | media_dir = 1; |
541 | // both path and absolute path are under /media | 556 | // both path and absolute path are under /media |
542 | if (strncmp(fname, "/media/", 7) != 0) { | 557 | if (strncmp(fname, "/media/", 7) != 0) { |
@@ -545,7 +560,7 @@ void fs_whitelist(void) { | |||
545 | } | 560 | } |
546 | } | 561 | } |
547 | else if (strncmp(new_name, "/mnt/", 5) == 0) { | 562 | else if (strncmp(new_name, "/mnt/", 5) == 0) { |
548 | entry->mnt_dir = 1; | 563 | entry->wldir = WLDIR_MNT; |
549 | mnt_dir = 1; | 564 | mnt_dir = 1; |
550 | // both path and absolute path are under /mnt | 565 | // both path and absolute path are under /mnt |
551 | if (strncmp(fname, "/mnt/", 5) != 0) { | 566 | if (strncmp(fname, "/mnt/", 5) != 0) { |
@@ -554,7 +569,7 @@ void fs_whitelist(void) { | |||
554 | } | 569 | } |
555 | } | 570 | } |
556 | else if (strncmp(new_name, "/var/", 5) == 0) { | 571 | else if (strncmp(new_name, "/var/", 5) == 0) { |
557 | entry->var_dir = 1; | 572 | entry->wldir = WLDIR_VAR; |
558 | var_dir = 1; | 573 | var_dir = 1; |
559 | // both path and absolute path are under /var | 574 | // both path and absolute path are under /var |
560 | // exceptions: /var/tmp, /var/run and /var/lock | 575 | // exceptions: /var/tmp, /var/run and /var/lock |
@@ -570,7 +585,7 @@ void fs_whitelist(void) { | |||
570 | } | 585 | } |
571 | } | 586 | } |
572 | else if (strncmp(new_name, "/dev/", 5) == 0) { | 587 | else if (strncmp(new_name, "/dev/", 5) == 0) { |
573 | entry->dev_dir = 1; | 588 | entry->wldir = WLDIR_DEV; |
574 | dev_dir = 1; | 589 | dev_dir = 1; |
575 | // special handling for /dev/shm | 590 | // special handling for /dev/shm |
576 | // on some platforms (Debian wheezy, Ubuntu 14.04), it is a symlink to /run/shm | 591 | // on some platforms (Debian wheezy, Ubuntu 14.04), it is a symlink to /run/shm |
@@ -591,7 +606,7 @@ void fs_whitelist(void) { | |||
591 | } | 606 | } |
592 | } | 607 | } |
593 | else if (strncmp(new_name, "/opt/", 5) == 0) { | 608 | else if (strncmp(new_name, "/opt/", 5) == 0) { |
594 | entry->opt_dir = 1; | 609 | entry->wldir = WLDIR_OPT; |
595 | opt_dir = 1; | 610 | opt_dir = 1; |
596 | // both path and absolute path are under /dev | 611 | // both path and absolute path are under /dev |
597 | if (strncmp(fname, "/opt/", 5) != 0) { | 612 | if (strncmp(fname, "/opt/", 5) != 0) { |
@@ -600,7 +615,7 @@ void fs_whitelist(void) { | |||
600 | } | 615 | } |
601 | } | 616 | } |
602 | else if (strncmp(new_name, "/srv/", 5) == 0) { | 617 | else if (strncmp(new_name, "/srv/", 5) == 0) { |
603 | entry->srv_dir = 1; | 618 | entry->wldir = WLDIR_SRV; |
604 | srv_dir = 1; | 619 | srv_dir = 1; |
605 | // both path and absolute path are under /srv | 620 | // both path and absolute path are under /srv |
606 | if (strncmp(fname, "/srv/", 5) != 0) { | 621 | if (strncmp(fname, "/srv/", 5) != 0) { |
@@ -609,7 +624,7 @@ void fs_whitelist(void) { | |||
609 | } | 624 | } |
610 | } | 625 | } |
611 | else if (strncmp(new_name, "/etc/", 5) == 0) { | 626 | else if (strncmp(new_name, "/etc/", 5) == 0) { |
612 | entry->etc_dir = 1; | 627 | entry->wldir = WLDIR_ETC; |
613 | etc_dir = 1; | 628 | etc_dir = 1; |
614 | // special handling for some of the symlinks | 629 | // special handling for some of the symlinks |
615 | if (strcmp(new_name, "/etc/localtime") == 0); | 630 | if (strcmp(new_name, "/etc/localtime") == 0); |
@@ -624,7 +639,7 @@ void fs_whitelist(void) { | |||
624 | } | 639 | } |
625 | } | 640 | } |
626 | else if (strncmp(new_name, "/usr/share/", 11) == 0) { | 641 | else if (strncmp(new_name, "/usr/share/", 11) == 0) { |
627 | entry->share_dir = 1; | 642 | entry->wldir = WLDIR_SHARE; |
628 | share_dir = 1; | 643 | share_dir = 1; |
629 | // both path and absolute path are under /etc | 644 | // both path and absolute path are under /etc |
630 | if (strncmp(fname, "/usr/share/", 11) != 0) { | 645 | if (strncmp(fname, "/usr/share/", 11) != 0) { |
@@ -633,7 +648,7 @@ void fs_whitelist(void) { | |||
633 | } | 648 | } |
634 | } | 649 | } |
635 | else if (strncmp(new_name, "/sys/module/", 12) == 0) { | 650 | else if (strncmp(new_name, "/sys/module/", 12) == 0) { |
636 | entry->module_dir = 1; | 651 | entry->wldir = WLDIR_MODULE; |
637 | module_dir = 1; | 652 | module_dir = 1; |
638 | // both path and absolute path are under /sys/module | 653 | // both path and absolute path are under /sys/module |
639 | if (strncmp(fname, "/sys/module/", 12) != 0) { | 654 | if (strncmp(fname, "/sys/module/", 12) != 0) { |
@@ -642,7 +657,7 @@ void fs_whitelist(void) { | |||
642 | } | 657 | } |
643 | } | 658 | } |
644 | else if (strncmp(new_name, runuser, runuser_len) == 0 && new_name[runuser_len] == '/') { | 659 | else if (strncmp(new_name, runuser, runuser_len) == 0 && new_name[runuser_len] == '/') { |
645 | entry->run_dir = 1; | 660 | entry->wldir = WLDIR_RUN; |
646 | run_dir = 1; | 661 | run_dir = 1; |
647 | // both path and absolute path are under /run/user/$uid | 662 | // both path and absolute path are under /run/user/$uid |
648 | if (strncmp(fname, runuser, runuser_len) != 0 || fname[runuser_len] != '/') { | 663 | if (strncmp(fname, runuser, runuser_len) != 0 || fname[runuser_len] != '/') { |
@@ -704,43 +719,28 @@ void fs_whitelist(void) { | |||
704 | free(nowhitelist); | 719 | free(nowhitelist); |
705 | 720 | ||
706 | EUID_ROOT(); | 721 | EUID_ROOT(); |
707 | // /home/user mountpoint | 722 | // /tmp mountpoint |
708 | if (home_dir) { | 723 | if (tmp_dir) { |
709 | // check if /home/user directory exists | 724 | // check if /tmp directory exists |
710 | if (stat(cfg.homedir, &s) == 0) { | 725 | if (stat("/tmp", &s) == 0) { |
711 | // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR | 726 | // keep a copy of real /tmp directory in RUN_WHITELIST_TMP_DIR |
712 | mkdir_attr(RUN_WHITELIST_HOME_USER_DIR, 0755, getuid(), getgid()); | 727 | mkdir_attr(RUN_WHITELIST_TMP_DIR, 1777, 0, 0); |
713 | int fd = safe_fd(cfg.homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 728 | if (mount("/tmp", RUN_WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
714 | if (fd == -1) | ||
715 | errExit("safe_fd"); | ||
716 | char *proc; | ||
717 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
718 | errExit("asprintf"); | ||
719 | if (mount(proc, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
720 | errExit("mount bind"); | 729 | errExit("mount bind"); |
721 | free(proc); | ||
722 | close(fd); | ||
723 | 730 | ||
724 | // mount a tmpfs and initialize /home/user, overrides --allusers | 731 | // mount tmpfs on /tmp |
725 | fs_private(); | 732 | if (arg_debug || arg_debug_whitelists) |
733 | printf("Mounting tmpfs on /tmp directory\n"); | ||
734 | if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=1777,gid=0") < 0) | ||
735 | errExit("mounting tmpfs on /tmp"); | ||
736 | fs_logger("tmpfs /tmp"); | ||
737 | |||
738 | // autowhitelist home directory if it is masked by the tmpfs | ||
739 | if (strncmp(cfg.homedir, "/tmp/", 5) == 0) | ||
740 | whitelist_home(WLDIR_TMP); | ||
726 | } | 741 | } |
727 | else | 742 | else |
728 | home_dir = 0; | 743 | tmp_dir = 0; |
729 | } | ||
730 | |||
731 | // /tmp mountpoint | ||
732 | if (tmp_dir) { | ||
733 | // keep a copy of real /tmp directory in RUN_WHITELIST_TMP_DIR | ||
734 | mkdir_attr(RUN_WHITELIST_TMP_DIR, 1777, 0, 0); | ||
735 | if (mount("/tmp", RUN_WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
736 | errExit("mount bind"); | ||
737 | |||
738 | // mount tmpfs on /tmp | ||
739 | if (arg_debug || arg_debug_whitelists) | ||
740 | printf("Mounting tmpfs on /tmp directory\n"); | ||
741 | if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=1777,gid=0") < 0) | ||
742 | errExit("mounting tmpfs on /tmp"); | ||
743 | fs_logger("tmpfs /tmp"); | ||
744 | } | 744 | } |
745 | 745 | ||
746 | // /media mountpoint | 746 | // /media mountpoint |
@@ -758,6 +758,10 @@ void fs_whitelist(void) { | |||
758 | if (mount("tmpfs", "/media", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 758 | if (mount("tmpfs", "/media", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) |
759 | errExit("mounting tmpfs on /media"); | 759 | errExit("mounting tmpfs on /media"); |
760 | fs_logger("tmpfs /media"); | 760 | fs_logger("tmpfs /media"); |
761 | |||
762 | // autowhitelist home directory if it is masked by the tmpfs | ||
763 | if (strncmp(cfg.homedir, "/media/", 7) == 0) | ||
764 | whitelist_home(WLDIR_MEDIA); | ||
761 | } | 765 | } |
762 | else | 766 | else |
763 | media_dir = 0; | 767 | media_dir = 0; |
@@ -778,40 +782,61 @@ void fs_whitelist(void) { | |||
778 | if (mount("tmpfs", "/mnt", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 782 | if (mount("tmpfs", "/mnt", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) |
779 | errExit("mounting tmpfs on /mnt"); | 783 | errExit("mounting tmpfs on /mnt"); |
780 | fs_logger("tmpfs /mnt"); | 784 | fs_logger("tmpfs /mnt"); |
785 | |||
786 | // autowhitelist home directory if it is masked by the tmpfs | ||
787 | if (strncmp(cfg.homedir, "/mnt/", 5) == 0) | ||
788 | whitelist_home(WLDIR_MNT); | ||
781 | } | 789 | } |
782 | else | 790 | else |
783 | mnt_dir = 0; | 791 | mnt_dir = 0; |
784 | } | 792 | } |
785 | 793 | ||
786 | |||
787 | // /var mountpoint | 794 | // /var mountpoint |
788 | if (var_dir) { | 795 | if (var_dir) { |
789 | // keep a copy of real /var directory in RUN_WHITELIST_VAR_DIR | 796 | // check if /var directory exists |
790 | mkdir_attr(RUN_WHITELIST_VAR_DIR, 0755, 0, 0); | 797 | if (stat("/var", &s) == 0) { |
791 | if (mount("/var", RUN_WHITELIST_VAR_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 798 | // keep a copy of real /var directory in RUN_WHITELIST_VAR_DIR |
792 | errExit("mount bind"); | 799 | mkdir_attr(RUN_WHITELIST_VAR_DIR, 0755, 0, 0); |
800 | if (mount("/var", RUN_WHITELIST_VAR_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
801 | errExit("mount bind"); | ||
793 | 802 | ||
794 | // mount tmpfs on /var | 803 | // mount tmpfs on /var |
795 | if (arg_debug || arg_debug_whitelists) | 804 | if (arg_debug || arg_debug_whitelists) |
796 | printf("Mounting tmpfs on /var directory\n"); | 805 | printf("Mounting tmpfs on /var directory\n"); |
797 | if (mount("tmpfs", "/var", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 806 | if (mount("tmpfs", "/var", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) |
798 | errExit("mounting tmpfs on /var"); | 807 | errExit("mounting tmpfs on /var"); |
799 | fs_logger("tmpfs /var"); | 808 | fs_logger("tmpfs /var"); |
809 | |||
810 | // autowhitelist home directory if it is masked by the tmpfs | ||
811 | if (strncmp(cfg.homedir, "/var/", 5) == 0) | ||
812 | whitelist_home(WLDIR_VAR); | ||
813 | } | ||
814 | else | ||
815 | var_dir = 0; | ||
800 | } | 816 | } |
801 | 817 | ||
802 | // /dev mountpoint | 818 | // /dev mountpoint |
803 | if (dev_dir) { | 819 | if (dev_dir) { |
804 | // keep a copy of real /dev directory in RUN_WHITELIST_DEV_DIR | 820 | // check if /dev directory exists |
805 | mkdir_attr(RUN_WHITELIST_DEV_DIR, 0755, 0, 0); | 821 | if (stat("/dev", &s) == 0) { |
806 | if (mount("/dev", RUN_WHITELIST_DEV_DIR, NULL, MS_BIND|MS_REC, "mode=755,gid=0") < 0) | 822 | // keep a copy of real /dev directory in RUN_WHITELIST_DEV_DIR |
807 | errExit("mount bind"); | 823 | mkdir_attr(RUN_WHITELIST_DEV_DIR, 0755, 0, 0); |
824 | if (mount("/dev", RUN_WHITELIST_DEV_DIR, NULL, MS_BIND|MS_REC, "mode=755,gid=0") < 0) | ||
825 | errExit("mount bind"); | ||
808 | 826 | ||
809 | // mount tmpfs on /dev | 827 | // mount tmpfs on /dev |
810 | if (arg_debug || arg_debug_whitelists) | 828 | if (arg_debug || arg_debug_whitelists) |
811 | printf("Mounting tmpfs on /dev directory\n"); | 829 | printf("Mounting tmpfs on /dev directory\n"); |
812 | if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 830 | if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) |
813 | errExit("mounting tmpfs on /dev"); | 831 | errExit("mounting tmpfs on /dev"); |
814 | fs_logger("tmpfs /dev"); | 832 | fs_logger("tmpfs /dev"); |
833 | |||
834 | // autowhitelist home directory if it is masked by the tmpfs | ||
835 | if (strncmp(cfg.homedir, "/dev/", 5) == 0) | ||
836 | whitelist_home(WLDIR_DEV); | ||
837 | } | ||
838 | else | ||
839 | dev_dir = 0; | ||
815 | } | 840 | } |
816 | 841 | ||
817 | // /opt mountpoint | 842 | // /opt mountpoint |
@@ -829,6 +854,10 @@ void fs_whitelist(void) { | |||
829 | if (mount("tmpfs", "/opt", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 854 | if (mount("tmpfs", "/opt", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) |
830 | errExit("mounting tmpfs on /opt"); | 855 | errExit("mounting tmpfs on /opt"); |
831 | fs_logger("tmpfs /opt"); | 856 | fs_logger("tmpfs /opt"); |
857 | |||
858 | // autowhitelist home directory if it is masked by the tmpfs | ||
859 | if (strncmp(cfg.homedir, "/opt/", 5) == 0) | ||
860 | whitelist_home(WLDIR_OPT); | ||
832 | } | 861 | } |
833 | else | 862 | else |
834 | opt_dir = 0; | 863 | opt_dir = 0; |
@@ -849,6 +878,10 @@ void fs_whitelist(void) { | |||
849 | if (mount("tmpfs", "/srv", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 878 | if (mount("tmpfs", "/srv", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) |
850 | errExit("mounting tmpfs on /srv"); | 879 | errExit("mounting tmpfs on /srv"); |
851 | fs_logger("tmpfs /srv"); | 880 | fs_logger("tmpfs /srv"); |
881 | |||
882 | // autowhitelist home directory if it is masked by the tmpfs | ||
883 | if (strncmp(cfg.homedir, "/srv/", 5) == 0) | ||
884 | whitelist_home(WLDIR_SRV); | ||
852 | } | 885 | } |
853 | else | 886 | else |
854 | srv_dir = 0; | 887 | srv_dir = 0; |
@@ -863,12 +896,16 @@ void fs_whitelist(void) { | |||
863 | if (mount("/etc", RUN_WHITELIST_ETC_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 896 | if (mount("/etc", RUN_WHITELIST_ETC_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
864 | errExit("mount bind"); | 897 | errExit("mount bind"); |
865 | 898 | ||
866 | // mount tmpfs on /srv | 899 | // mount tmpfs on /etc |
867 | if (arg_debug || arg_debug_whitelists) | 900 | if (arg_debug || arg_debug_whitelists) |
868 | printf("Mounting tmpfs on /etc directory\n"); | 901 | printf("Mounting tmpfs on /etc directory\n"); |
869 | if (mount("tmpfs", "/etc", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 902 | if (mount("tmpfs", "/etc", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) |
870 | errExit("mounting tmpfs on /etc"); | 903 | errExit("mounting tmpfs on /etc"); |
871 | fs_logger("tmpfs /etc"); | 904 | fs_logger("tmpfs /etc"); |
905 | |||
906 | // autowhitelist home directory if it is masked by the tmpfs | ||
907 | if (strncmp(cfg.homedir, "/etc/", 5) == 0) | ||
908 | whitelist_home(WLDIR_ETC); | ||
872 | } | 909 | } |
873 | else | 910 | else |
874 | etc_dir = 0; | 911 | etc_dir = 0; |
@@ -889,6 +926,10 @@ void fs_whitelist(void) { | |||
889 | if (mount("tmpfs", "/usr/share", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 926 | if (mount("tmpfs", "/usr/share", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) |
890 | errExit("mounting tmpfs on /usr/share"); | 927 | errExit("mounting tmpfs on /usr/share"); |
891 | fs_logger("tmpfs /usr/share"); | 928 | fs_logger("tmpfs /usr/share"); |
929 | |||
930 | // autowhitelist home directory if it is masked by the tmpfs | ||
931 | if (strncmp(cfg.homedir, "/usr/share/", 11) == 0) | ||
932 | whitelist_home(WLDIR_SHARE); | ||
892 | } | 933 | } |
893 | else | 934 | else |
894 | share_dir = 0; | 935 | share_dir = 0; |
@@ -914,7 +955,7 @@ void fs_whitelist(void) { | |||
914 | module_dir = 0; | 955 | module_dir = 0; |
915 | } | 956 | } |
916 | 957 | ||
917 | // /run/user mountpoint | 958 | // /run/user/$uid mountpoint |
918 | if (run_dir) { | 959 | if (run_dir) { |
919 | // check if /run/user/$uid directory exists | 960 | // check if /run/user/$uid directory exists |
920 | if (stat(runuser, &s) == 0) { | 961 | if (stat(runuser, &s) == 0) { |
@@ -933,11 +974,38 @@ void fs_whitelist(void) { | |||
933 | errExit("mounting tmpfs on /run/user/<uid>"); | 974 | errExit("mounting tmpfs on /run/user/<uid>"); |
934 | free(options); | 975 | free(options); |
935 | fs_logger2("tmpfs", runuser); | 976 | fs_logger2("tmpfs", runuser); |
977 | |||
978 | // autowhitelist home directory if it is masked by the tmpfs | ||
979 | if (strncmp(cfg.homedir, runuser, runuser_len) == 0 && cfg.homedir[runuser_len] == '/') | ||
980 | whitelist_home(WLDIR_RUN); | ||
936 | } | 981 | } |
937 | else | 982 | else |
938 | run_dir = 0; | 983 | run_dir = 0; |
939 | } | 984 | } |
940 | 985 | ||
986 | // home mountpoint | ||
987 | if (home_dir) { | ||
988 | // check if home directory exists | ||
989 | if (stat(cfg.homedir, &s) == 0) { | ||
990 | // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR | ||
991 | mkdir_attr(RUN_WHITELIST_HOME_USER_DIR, 0755, getuid(), getgid()); | ||
992 | int fd = safe_fd(cfg.homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
993 | if (fd == -1) | ||
994 | errExit("safe_fd"); | ||
995 | char *proc; | ||
996 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
997 | errExit("asprintf"); | ||
998 | if (mount(proc, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
999 | errExit("mount bind"); | ||
1000 | free(proc); | ||
1001 | close(fd); | ||
1002 | |||
1003 | // mount a tmpfs and initialize home directory, overrides --allusers | ||
1004 | fs_private(); | ||
1005 | } | ||
1006 | else | ||
1007 | home_dir = 0; | ||
1008 | } | ||
941 | 1009 | ||
942 | // go through profile rules again, and interpret whitelist commands | 1010 | // go through profile rules again, and interpret whitelist commands |
943 | entry = cfg.profile; | 1011 | entry = cfg.profile; |
@@ -957,6 +1025,7 @@ void fs_whitelist(void) { | |||
957 | // if the link is already there, do not bother | 1025 | // if the link is already there, do not bother |
958 | if (lstat(entry->link, &s) != 0) { | 1026 | if (lstat(entry->link, &s) != 0) { |
959 | // create the path if necessary | 1027 | // create the path if necessary |
1028 | // entry->link has no trailing slashes or single dots | ||
960 | int fd = mkpath(entry->link, 0755); | 1029 | int fd = mkpath(entry->link, 0755); |
961 | if (fd == -1) { | 1030 | if (fd == -1) { |
962 | if (arg_debug || arg_debug_whitelists) | 1031 | if (arg_debug || arg_debug_whitelists) |
@@ -1064,7 +1133,7 @@ void fs_whitelist(void) { | |||
1064 | fs_logger2("tmpfs", RUN_WHITELIST_MODULE_DIR); | 1133 | fs_logger2("tmpfs", RUN_WHITELIST_MODULE_DIR); |
1065 | } | 1134 | } |
1066 | 1135 | ||
1067 | // mask the real /run/user/$uid directory, currently mounted on RUN_WHITELIST_MODULE_DIR | 1136 | // mask the real /run/user/$uid directory, currently mounted on RUN_WHITELIST_RUN_USER_DIR |
1068 | if (run_dir) { | 1137 | if (run_dir) { |
1069 | if (mount("tmpfs", RUN_WHITELIST_RUN_USER_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 1138 | if (mount("tmpfs", RUN_WHITELIST_RUN_USER_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) |
1070 | errExit("mount tmpfs"); | 1139 | errExit("mount tmpfs"); |