diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/appimage.c | 11 | ||||
-rw-r--r-- | src/firejail/checkcfg.c | 2 | ||||
-rw-r--r-- | src/firejail/firejail.h | 1 | ||||
-rw-r--r-- | src/firejail/fs.c | 9 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 88 | ||||
-rw-r--r-- | src/firejail/fs_whitelist.c | 10 | ||||
-rw-r--r-- | src/firejail/main.c | 51 | ||||
-rw-r--r-- | src/firejail/restrict_users.c | 16 |
8 files changed, 142 insertions, 46 deletions
diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c index 7c60d9189..1da765d8f 100644 --- a/src/firejail/appimage.c +++ b/src/firejail/appimage.c | |||
@@ -126,16 +126,25 @@ void appimage_set(const char *appimage) { | |||
126 | printf("appimage mounted on %s\n", mntdir); | 126 | printf("appimage mounted on %s\n", mntdir); |
127 | EUID_USER(); | 127 | EUID_USER(); |
128 | 128 | ||
129 | char* abspath = realpath(appimage, NULL); | ||
130 | if (abspath == NULL) | ||
131 | errExit("Failed to obtain absolute path"); | ||
132 | |||
129 | // set environment | 133 | // set environment |
130 | if (setenv("APPIMAGE", appimage, 1) < 0) | 134 | if (setenv("APPIMAGE", abspath, 1) < 0) |
131 | errExit("setenv"); | 135 | errExit("setenv"); |
132 | if (mntdir && setenv("APPDIR", mntdir, 1) < 0) | 136 | if (mntdir && setenv("APPDIR", mntdir, 1) < 0) |
133 | errExit("setenv"); | 137 | errExit("setenv"); |
138 | if (size != 0 && setenv("ARGV0", appimage, 1) < 0) | ||
139 | errExit("setenv"); | ||
140 | if (setenv("OWD", cfg.cwd, 1) < 0) | ||
141 | errExit("setenv"); | ||
134 | 142 | ||
135 | // build new command line | 143 | // build new command line |
136 | if (asprintf(&cfg.command_line, "%s/AppRun", mntdir) == -1) | 144 | if (asprintf(&cfg.command_line, "%s/AppRun", mntdir) == -1) |
137 | errExit("asprintf"); | 145 | errExit("asprintf"); |
138 | 146 | ||
147 | free(abspath); | ||
139 | free(mode); | 148 | free(mode); |
140 | #ifdef HAVE_GCOV | 149 | #ifdef HAVE_GCOV |
141 | __gcov_flush(); | 150 | __gcov_flush(); |
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c index f94b95d60..84054fe76 100644 --- a/src/firejail/checkcfg.c +++ b/src/firejail/checkcfg.c | |||
@@ -50,6 +50,7 @@ int checkcfg(int val) { | |||
50 | cfg_val[CFG_DISABLE_MNT] = 0; | 50 | cfg_val[CFG_DISABLE_MNT] = 0; |
51 | cfg_val[CFG_ARP_PROBES] = DEFAULT_ARP_PROBES; | 51 | cfg_val[CFG_ARP_PROBES] = DEFAULT_ARP_PROBES; |
52 | cfg_val[CFG_XPRA_ATTACH] = 0; | 52 | cfg_val[CFG_XPRA_ATTACH] = 0; |
53 | cfg_val[CFG_HOMEDIR_SYMLINK] = 0; | ||
53 | 54 | ||
54 | // open configuration file | 55 | // open configuration file |
55 | const char *fname = SYSCONFDIR "/firejail.config"; | 56 | const char *fname = SYSCONFDIR "/firejail.config"; |
@@ -85,6 +86,7 @@ int checkcfg(int val) { | |||
85 | ptr = line_remove_spaces(buf); | 86 | ptr = line_remove_spaces(buf); |
86 | if (!ptr) | 87 | if (!ptr) |
87 | continue; | 88 | continue; |
89 | PARSE_YESNO(CFG_HOMEDIR_SYMLINK, "homedir-symlink") | ||
88 | PARSE_YESNO(CFG_FILE_TRANSFER, "file-transfer") | 90 | PARSE_YESNO(CFG_FILE_TRANSFER, "file-transfer") |
89 | PARSE_YESNO(CFG_DBUS, "dbus") | 91 | PARSE_YESNO(CFG_DBUS, "dbus") |
90 | PARSE_YESNO(CFG_JOIN, "join") | 92 | PARSE_YESNO(CFG_JOIN, "join") |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 7664c8037..4bd70697e 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -720,6 +720,7 @@ enum { | |||
720 | CFG_PRIVATE_CACHE, | 720 | CFG_PRIVATE_CACHE, |
721 | CFG_CGROUP, | 721 | CFG_CGROUP, |
722 | CFG_NAME_CHANGE, | 722 | CFG_NAME_CHANGE, |
723 | CFG_HOMEDIR_SYMLINK, | ||
723 | // CFG_FILE_COPY_LIMIT - file copy limit handled using setenv/getenv | 724 | // CFG_FILE_COPY_LIMIT - file copy limit handled using setenv/getenv |
724 | CFG_MAX // this should always be the last entry | 725 | CFG_MAX // this should always be the last entry |
725 | }; | 726 | }; |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index fe0427466..ce2ca5e2a 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -441,6 +441,8 @@ static int get_mount_flags(const char *path, unsigned long *flags) { | |||
441 | // mount a writable tmpfs on directory | 441 | // mount a writable tmpfs on directory |
442 | void fs_tmpfs(const char *dir, unsigned check_owner) { | 442 | void fs_tmpfs(const char *dir, unsigned check_owner) { |
443 | assert(dir); | 443 | assert(dir); |
444 | if (arg_debug) | ||
445 | printf("Mounting tmpfs on %s\n", dir); | ||
444 | // get a file descriptor for dir, fails if there is any symlink | 446 | // get a file descriptor for dir, fails if there is any symlink |
445 | int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 447 | int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
446 | if (fd == -1) | 448 | if (fd == -1) |
@@ -449,12 +451,9 @@ void fs_tmpfs(const char *dir, unsigned check_owner) { | |||
449 | if (fstat(fd, &s) == -1) | 451 | if (fstat(fd, &s) == -1) |
450 | errExit("fstat"); | 452 | errExit("fstat"); |
451 | if (check_owner && s.st_uid != getuid()) { | 453 | if (check_owner && s.st_uid != getuid()) { |
452 | fwarning("no tmpfs mounted on %s: not owned by the current user\n", dir); | 454 | fprintf(stderr, "Error: cannot mount tmpfs on %s: not owned by the current user\n", dir); |
453 | close(fd); | 455 | exit(1); |
454 | return; | ||
455 | } | 456 | } |
456 | if (arg_debug) | ||
457 | printf("Mounting tmpfs on %s\n", dir); | ||
458 | // preserve ownership, mode | 457 | // preserve ownership, mode |
459 | char *options; | 458 | char *options; |
460 | if (asprintf(&options, "mode=%o,uid=%u,gid=%u", s.st_mode & 07777, s.st_uid, s.st_gid) == -1) | 459 | if (asprintf(&options, "mode=%o,uid=%u,gid=%u", s.st_mode & 07777, s.st_uid, s.st_gid) == -1) |
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 3f6d78db4..69ad5e2c8 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -239,13 +239,16 @@ void fs_private_homedir(void) { | |||
239 | // mount bind private_homedir on top of homedir | 239 | // mount bind private_homedir on top of homedir |
240 | if (arg_debug) | 240 | if (arg_debug) |
241 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); | 241 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); |
242 | // get a file descriptor for private_homedir, fails if there is any symlink | 242 | // get file descriptors for homedir and private_homedir, fails if there is any symlink |
243 | int fd = safe_fd(private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 243 | int src = safe_fd(private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
244 | if (fd == -1) | 244 | if (src == -1) |
245 | errExit("safe_fd"); | ||
246 | int dst = safe_fd(homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
247 | if (dst == -1) | ||
245 | errExit("safe_fd"); | 248 | errExit("safe_fd"); |
246 | // check if new home directory is owned by the user | 249 | // check if new home directory is owned by the user |
247 | struct stat s; | 250 | struct stat s; |
248 | if (fstat(fd, &s) == -1) | 251 | if (fstat(src, &s) == -1) |
249 | errExit("fstat"); | 252 | errExit("fstat"); |
250 | if (s.st_uid != getuid()) { | 253 | if (s.st_uid != getuid()) { |
251 | fprintf(stderr, "Error: private directory is not owned by the current user\n"); | 254 | fprintf(stderr, "Error: private directory is not owned by the current user\n"); |
@@ -253,17 +256,27 @@ void fs_private_homedir(void) { | |||
253 | } | 256 | } |
254 | if ((S_IRWXU & s.st_mode) != S_IRWXU) | 257 | if ((S_IRWXU & s.st_mode) != S_IRWXU) |
255 | fwarning("no full permissions on private directory\n"); | 258 | fwarning("no full permissions on private directory\n"); |
256 | // mount via the link in /proc/self/fd | 259 | // mount via the links in /proc/self/fd |
257 | char *proc; | 260 | char *proc_src, *proc_dst; |
258 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 261 | if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1) |
262 | errExit("asprintf"); | ||
263 | if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1) | ||
259 | errExit("asprintf"); | 264 | errExit("asprintf"); |
260 | if (mount(proc, homedir, NULL, MS_NOSUID | MS_NODEV | MS_BIND | MS_REC, NULL) < 0) | 265 | if (mount(proc_src, proc_dst, NULL, MS_NOSUID | MS_NODEV | MS_BIND | MS_REC, NULL) < 0) |
261 | errExit("mount bind"); | 266 | errExit("mount bind"); |
262 | free(proc); | 267 | free(proc_src); |
263 | close(fd); | 268 | free(proc_dst); |
264 | 269 | close(src); | |
265 | fs_logger3("mount-bind", private_homedir, cfg.homedir); | 270 | close(dst); |
266 | fs_logger2("whitelist", cfg.homedir); | 271 | // check /proc/self/mountinfo to confirm the mount is ok |
272 | MountData *mptr = get_last_mount(); | ||
273 | size_t len = strlen(homedir); | ||
274 | if (strncmp(mptr->dir, homedir, len) != 0 || | ||
275 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) | ||
276 | errLogExit("invalid private mount"); | ||
277 | |||
278 | fs_logger3("mount-bind", private_homedir, homedir); | ||
279 | fs_logger2("whitelist", homedir); | ||
267 | // preserve mode and ownership | 280 | // preserve mode and ownership |
268 | // if (chown(homedir, s.st_uid, s.st_gid) == -1) | 281 | // if (chown(homedir, s.st_uid, s.st_gid) == -1) |
269 | // errExit("mount-bind chown"); | 282 | // errExit("mount-bind chown"); |
@@ -310,13 +323,13 @@ void fs_private(void) { | |||
310 | int aflag = store_asoundrc(); | 323 | int aflag = store_asoundrc(); |
311 | 324 | ||
312 | // mask /home | 325 | // mask /home |
313 | if (arg_debug) | ||
314 | printf("Mounting a new /home directory\n"); | ||
315 | if (u == 0 && arg_allusers) // allow --allusers when starting the sandbox as root | 326 | if (u == 0 && arg_allusers) // allow --allusers when starting the sandbox as root |
316 | ; | 327 | ; |
317 | else { | 328 | else { |
329 | if (arg_debug) | ||
330 | printf("Mounting a new /home directory\n"); | ||
318 | if (arg_allusers) | 331 | if (arg_allusers) |
319 | fwarning("--allusers disabled by --private or --whitelist\n"); | 332 | fwarning("allusers option disabled by private or whitelist option\n"); |
320 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME, "mode=755,gid=0") < 0) | 333 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME, "mode=755,gid=0") < 0) |
321 | errExit("mounting home directory"); | 334 | errExit("mounting home directory"); |
322 | fs_logger("tmpfs /home"); | 335 | fs_logger("tmpfs /home"); |
@@ -330,19 +343,24 @@ void fs_private(void) { | |||
330 | fs_logger("tmpfs /root"); | 343 | fs_logger("tmpfs /root"); |
331 | 344 | ||
332 | if (u != 0) { | 345 | if (u != 0) { |
333 | // create /home/user | 346 | if (strncmp(homedir, "/home/", 6) == 0) { |
334 | if (arg_debug) | 347 | // create /home/user |
335 | printf("Create a new user directory\n"); | 348 | if (arg_debug) |
336 | if (mkdir(homedir, S_IRWXU) == -1) { | 349 | printf("Create a new user directory\n"); |
337 | if (mkpath_as_root(homedir) == -1) | 350 | if (mkdir(homedir, S_IRWXU) == -1) { |
338 | errExit("mkpath"); | 351 | if (mkpath_as_root(homedir) == -1) |
339 | if (mkdir(homedir, S_IRWXU) == -1 && errno != EEXIST) | 352 | errExit("mkpath"); |
340 | errExit("mkdir"); | 353 | if (mkdir(homedir, S_IRWXU) == -1 && errno != EEXIST) |
354 | errExit("mkdir"); | ||
355 | } | ||
356 | if (chown(homedir, u, g) < 0) | ||
357 | errExit("chown"); | ||
358 | fs_logger2("mkdir", homedir); | ||
359 | fs_logger2("tmpfs", homedir); | ||
341 | } | 360 | } |
342 | if (chown(homedir, u, g) < 0) | 361 | else |
343 | errExit("chown"); | 362 | // user directory is outside /home, mask it as well |
344 | fs_logger2("mkdir", homedir); | 363 | fs_tmpfs(homedir, 1); |
345 | fs_logger2("tmpfs", homedir); | ||
346 | } | 364 | } |
347 | 365 | ||
348 | skel(homedir, u, g); | 366 | skel(homedir, u, g); |
@@ -528,8 +546,20 @@ void fs_private_home_list(void) { | |||
528 | if (arg_debug) | 546 | if (arg_debug) |
529 | printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir); | 547 | printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir); |
530 | 548 | ||
531 | if (mount(RUN_HOME_DIR, homedir, NULL, MS_BIND|MS_REC, NULL) < 0) | 549 | int fd = safe_fd(homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
550 | if (fd == -1) | ||
551 | errExit("safe_fd"); | ||
552 | char *proc; | ||
553 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
554 | errExit("asprintf"); | ||
555 | if (mount(RUN_HOME_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
532 | errExit("mount bind"); | 556 | errExit("mount bind"); |
557 | free(proc); | ||
558 | close(fd); | ||
559 | // check /proc/self/mountinfo to confirm the mount is ok | ||
560 | MountData *mptr = get_last_mount(); | ||
561 | if (strcmp(mptr->dir, homedir) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) | ||
562 | errLogExit("invalid private-home mount"); | ||
533 | fs_logger2("tmpfs", homedir); | 563 | fs_logger2("tmpfs", homedir); |
534 | 564 | ||
535 | if (uid != 0) { | 565 | if (uid != 0) { |
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index c8fa8c72b..a950d1124 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -710,8 +710,16 @@ void fs_whitelist(void) { | |||
710 | if (stat(cfg.homedir, &s) == 0) { | 710 | if (stat(cfg.homedir, &s) == 0) { |
711 | // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR | 711 | // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR |
712 | mkdir_attr(RUN_WHITELIST_HOME_USER_DIR, 0755, getuid(), getgid()); | 712 | mkdir_attr(RUN_WHITELIST_HOME_USER_DIR, 0755, getuid(), getgid()); |
713 | if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 713 | int fd = safe_fd(cfg.homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
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) | ||
714 | errExit("mount bind"); | 720 | errExit("mount bind"); |
721 | free(proc); | ||
722 | close(fd); | ||
715 | 723 | ||
716 | // mount a tmpfs and initialize /home/user, overrides --allusers | 724 | // mount a tmpfs and initialize /home/user, overrides --allusers |
717 | fs_private(); | 725 | fs_private(); |
diff --git a/src/firejail/main.c b/src/firejail/main.c index 1e2488062..d00147c74 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -26,7 +26,6 @@ | |||
26 | #include <sys/mount.h> | 26 | #include <sys/mount.h> |
27 | #include <sys/wait.h> | 27 | #include <sys/wait.h> |
28 | #include <sys/stat.h> | 28 | #include <sys/stat.h> |
29 | #include <fcntl.h> | ||
30 | #include <dirent.h> | 29 | #include <dirent.h> |
31 | #include <pwd.h> | 30 | #include <pwd.h> |
32 | #include <errno.h> | 31 | #include <errno.h> |
@@ -38,6 +37,11 @@ | |||
38 | #include <net/if.h> | 37 | #include <net/if.h> |
39 | #include <sys/utsname.h> | 38 | #include <sys/utsname.h> |
40 | 39 | ||
40 | #include <fcntl.h> | ||
41 | #ifndef O_PATH | ||
42 | #define O_PATH 010000000 | ||
43 | #endif | ||
44 | |||
41 | #ifdef __ia64__ | 45 | #ifdef __ia64__ |
42 | /* clone(2) has a different interface on ia64, as it needs to know | 46 | /* clone(2) has a different interface on ia64, as it needs to know |
43 | the size of the stack */ | 47 | the size of the stack */ |
@@ -242,6 +246,41 @@ static pid_t require_pid(const char *name) { | |||
242 | return pid; | 246 | return pid; |
243 | } | 247 | } |
244 | 248 | ||
249 | // return 1 if there is a link somewhere in path of directory | ||
250 | static int has_link(const char *dir) { | ||
251 | assert(dir); | ||
252 | int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
253 | if (fd == -1) { | ||
254 | if (errno == ENOTDIR && is_dir(dir)) | ||
255 | return 1; | ||
256 | } | ||
257 | else | ||
258 | close(fd); | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static void build_cfg_homedir(const char *dir) { | ||
263 | EUID_ASSERT(); | ||
264 | assert(dir); | ||
265 | if (dir[0] != '/') { | ||
266 | fprintf(stderr, "Error: invalid user directory \"%s\"\n", dir); | ||
267 | exit(1); | ||
268 | } | ||
269 | // symlinks are rejected in many places, provide a solution for home directories | ||
270 | if (checkcfg(CFG_HOMEDIR_SYMLINK)) { | ||
271 | cfg.homedir = realpath(dir, NULL); | ||
272 | if (cfg.homedir) | ||
273 | return; | ||
274 | } | ||
275 | else if (has_link(dir)) { | ||
276 | fprintf(stderr, "Error: path of user directory contains a symbolic link. " | ||
277 | "Please provide resolved path in password database (/etc/passwd) " | ||
278 | "or enable symbolic link resolution in Firejail configuration file.\n"); | ||
279 | exit(1); | ||
280 | } | ||
281 | cfg.homedir = clean_pathname(dir); | ||
282 | } | ||
283 | |||
245 | // init configuration | 284 | // init configuration |
246 | static void init_cfg(int argc, char **argv) { | 285 | static void init_cfg(int argc, char **argv) { |
247 | EUID_ASSERT(); | 286 | EUID_ASSERT(); |
@@ -265,15 +304,12 @@ static void init_cfg(int argc, char **argv) { | |||
265 | errExit("strdup"); | 304 | errExit("strdup"); |
266 | 305 | ||
267 | // build home directory name | 306 | // build home directory name |
268 | cfg.homedir = NULL; | 307 | if (pw->pw_dir == NULL) { |
269 | if (pw->pw_dir != NULL) { | ||
270 | cfg.homedir = clean_pathname(pw->pw_dir); | ||
271 | assert(cfg.homedir); | ||
272 | } | ||
273 | else { | ||
274 | fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username); | 308 | fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username); |
275 | exit(1); | 309 | exit(1); |
276 | } | 310 | } |
311 | build_cfg_homedir(pw->pw_dir); | ||
312 | assert(cfg.homedir); | ||
277 | 313 | ||
278 | cfg.cwd = getcwd(NULL, 0); | 314 | cfg.cwd = getcwd(NULL, 0); |
279 | if (!cfg.cwd && errno != ENOENT) | 315 | if (!cfg.cwd && errno != ENOENT) |
@@ -926,7 +962,6 @@ int main(int argc, char **argv) { | |||
926 | 962 | ||
927 | // check if the user is allowed to use firejail | 963 | // check if the user is allowed to use firejail |
928 | init_cfg(argc, argv); | 964 | init_cfg(argc, argv); |
929 | assert(cfg.homedir); | ||
930 | 965 | ||
931 | // get starting timestamp, process --quiet | 966 | // get starting timestamp, process --quiet |
932 | start_timestamp = getticks(); | 967 | start_timestamp = getticks(); |
diff --git a/src/firejail/restrict_users.c b/src/firejail/restrict_users.c index 5c5ace90b..ee2e497cb 100644 --- a/src/firejail/restrict_users.c +++ b/src/firejail/restrict_users.c | |||
@@ -25,9 +25,13 @@ | |||
25 | #include <fnmatch.h> | 25 | #include <fnmatch.h> |
26 | #include <glob.h> | 26 | #include <glob.h> |
27 | #include <dirent.h> | 27 | #include <dirent.h> |
28 | #include <fcntl.h> | ||
29 | #include <errno.h> | 28 | #include <errno.h> |
30 | 29 | ||
30 | #include <fcntl.h> | ||
31 | #ifndef O_PATH | ||
32 | # define O_PATH 010000000 | ||
33 | #endif | ||
34 | |||
31 | #define MAXBUF 1024 | 35 | #define MAXBUF 1024 |
32 | 36 | ||
33 | // linked list of users | 37 | // linked list of users |
@@ -79,8 +83,16 @@ static void sanitize_home(void) { | |||
79 | errExit("mkdir"); | 83 | errExit("mkdir"); |
80 | 84 | ||
81 | // keep a copy of the user home directory | 85 | // keep a copy of the user home directory |
82 | if (mount(cfg.homedir, RUN_WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 86 | int fd = safe_fd(cfg.homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
87 | if (fd == -1) | ||
88 | errExit("safe_fd"); | ||
89 | char *proc; | ||
90 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
91 | errExit("asprintf"); | ||
92 | if (mount(proc, RUN_WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
83 | errExit("mount bind"); | 93 | errExit("mount bind"); |
94 | free(proc); | ||
95 | close(fd); | ||
84 | 96 | ||
85 | // mount tmpfs in the new home | 97 | // mount tmpfs in the new home |
86 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) | 98 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) |