aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2019-07-25 11:57:36 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2019-07-25 11:57:36 +0200
commit70fafcd6dcaa28a546f0a15a2a47b087b9c0a514 (patch)
treeaa8f3b74b61207e86f0ff085a2474758c2114e5b
parentfix verbosity for non-authorized user (diff)
downloadfirejail-70fafcd6dcaa28a546f0a15a2a47b087b9c0a514.tar.gz
firejail-70fafcd6dcaa28a546f0a15a2a47b087b9c0a514.tar.zst
firejail-70fafcd6dcaa28a546f0a15a2a47b087b9c0a514.zip
fix whitelisting for homedirs outside /home
-rw-r--r--src/firejail/firejail.h28
-rw-r--r--src/firejail/fs_whitelist.c235
2 files changed, 167 insertions, 96 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 4bd70697e..d547f9840 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -124,19 +124,21 @@ typedef struct profile_entry_t {
124 124
125 // whitelist command parameters 125 // whitelist command parameters
126 char *link; // link name - set if the file is a link 126 char *link; // link name - set if the file is a link
127 unsigned home_dir:1; // whitelist in /home/user directory 127 enum {
128 unsigned tmp_dir:1; // whitelist in /tmp directory 128 WLDIR_HOME = 1, // whitelist in home directory
129 unsigned media_dir:1; // whitelist in /media directory 129 WLDIR_TMP, // whitelist in /tmp directory
130 unsigned mnt_dir:1; // whitelist in /mnt directory 130 WLDIR_MEDIA, // whitelist in /media directory
131 unsigned var_dir:1; // whitelist in /var directory 131 WLDIR_MNT, // whitelist in /mnt directory
132 unsigned dev_dir:1; // whitelist in /dev directory 132 WLDIR_VAR, // whitelist in /var directory
133 unsigned opt_dir:1; // whitelist in /opt directory 133 WLDIR_DEV, // whitelist in /dev directory
134 unsigned srv_dir:1; // whitelist in /srv directory 134 WLDIR_OPT, // whitelist in /opt directory
135 unsigned etc_dir:1; // whitelist in /etc directory 135 WLDIR_SRV, // whitelist in /srv directory
136 unsigned share_dir:1; // whitelist in /usr/share directory 136 WLDIR_ETC, // whitelist in /etc directory
137 unsigned module_dir:1; // whitelist in /sys/module directory 137 WLDIR_SHARE, // whitelist in /usr/share directory
138 unsigned run_dir:1; // whitelist in /run/user/$uid directory 138 WLDIR_MODULE, // whitelist in /sys/module directory
139}ProfileEntry; 139 WLDIR_RUN // whitelist in /run/user/$uid directory
140 } wldir;
141} ProfileEntry;
140 142
141typedef struct config_t { 143typedef struct config_t {
142 // user data 144 // user data
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
333static 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
333void fs_whitelist(void) { 348void 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");