diff options
author | smitsohu <smitsohu@gmail.com> | 2019-10-01 15:46:12 +0200 |
---|---|---|
committer | smitsohu <smitsohu@gmail.com> | 2019-10-01 15:46:12 +0200 |
commit | 70c8924ebaf51387f74eba3c443ef7e870d2afc9 (patch) | |
tree | da4a8e53bc0d6311739e0ee6357a55a0d954f063 /src | |
parent | improve variable names (diff) | |
download | firejail-70c8924ebaf51387f74eba3c443ef7e870d2afc9.tar.gz firejail-70c8924ebaf51387f74eba3c443ef7e870d2afc9.tar.zst firejail-70c8924ebaf51387f74eba3c443ef7e870d2afc9.zip |
base checks and mounts on same file descriptor
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/chroot.c | 190 | ||||
-rw-r--r-- | src/firejail/firejail.h | 3 | ||||
-rw-r--r-- | src/firejail/main.c | 32 |
3 files changed, 91 insertions, 134 deletions
diff --git a/src/firejail/chroot.c b/src/firejail/chroot.c index 4eff84494..8a57dee35 100644 --- a/src/firejail/chroot.c +++ b/src/firejail/chroot.c | |||
@@ -21,7 +21,6 @@ | |||
21 | #ifdef HAVE_CHROOT | 21 | #ifdef HAVE_CHROOT |
22 | #include "firejail.h" | 22 | #include "firejail.h" |
23 | #include <sys/mount.h> | 23 | #include <sys/mount.h> |
24 | #include <sys/stat.h> | ||
25 | #include <sys/sendfile.h> | 24 | #include <sys/sendfile.h> |
26 | #include <errno.h> | 25 | #include <errno.h> |
27 | 26 | ||
@@ -31,107 +30,33 @@ | |||
31 | #endif | 30 | #endif |
32 | 31 | ||
33 | 32 | ||
34 | // exit if error | 33 | // exit if error, return resolved chroot path |
35 | static void fs_check_chroot_subdir(const char *subdir, int parentfd, int check_writable) { | 34 | char *fs_check_chroot_dir(const char *rootdir) { |
36 | assert(subdir); | ||
37 | int fd = openat(parentfd, subdir, O_PATH|O_CLOEXEC); | ||
38 | if (fd == -1) { | ||
39 | if (errno == ENOENT) | ||
40 | fprintf(stderr, "Error: cannot find /%s in chroot directory\n", subdir); | ||
41 | else { | ||
42 | perror("open"); | ||
43 | fprintf(stderr, "Error: cannot open /%s in chroot directory\n", subdir); | ||
44 | } | ||
45 | exit(1); | ||
46 | } | ||
47 | struct stat s; | ||
48 | if (fstat(fd, &s) == -1) | ||
49 | errExit("fstat"); | ||
50 | close(fd); | ||
51 | if (!S_ISDIR(s.st_mode) || s.st_uid != 0) { | ||
52 | fprintf(stderr, "Error: chroot /%s should be a directory owned by root\n", subdir); | ||
53 | exit(1); | ||
54 | } | ||
55 | if (check_writable && ((S_IWGRP|S_IWOTH) & s.st_mode) != 0) { | ||
56 | fprintf(stderr, "Error: only root user should be given write permission on chroot /%s\n", subdir); | ||
57 | exit(1); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | // exit if error | ||
62 | void fs_check_chroot_dir(const char *rootdir) { | ||
63 | EUID_ASSERT(); | 35 | EUID_ASSERT(); |
64 | assert(rootdir); | 36 | assert(rootdir); |
37 | if (strstr(rootdir, "..") || | ||
38 | is_link(rootdir) || | ||
39 | !is_dir(rootdir)) | ||
40 | goto errout; | ||
41 | |||
42 | // check chroot dirname exists, chrooting into the root directory is not allowed | ||
43 | char *rpath = realpath(rootdir, NULL); | ||
44 | if (rpath == NULL || strcmp(rpath, "/") == 0) | ||
45 | goto errout; | ||
65 | 46 | ||
66 | char *overlay; | 47 | char *overlay; |
67 | if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1) | 48 | if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1) |
68 | errExit("asprintf"); | 49 | errExit("asprintf"); |
69 | if (strncmp(rootdir, overlay, strlen(overlay)) == 0) { | 50 | if (strncmp(rpath, overlay, strlen(overlay)) == 0) { |
70 | fprintf(stderr, "Error: invalid chroot directory: no directories in %s are allowed\n", overlay); | 51 | fprintf(stderr, "Error: invalid chroot directory: no directories in %s are allowed\n", overlay); |
71 | exit(1); | 52 | exit(1); |
72 | } | 53 | } |
73 | free(overlay); | 54 | free(overlay); |
55 | return rpath; | ||
74 | 56 | ||
75 | // fails if there is any symlink or if rootdir is not a directory | 57 | errout: |
76 | int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 58 | fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir); |
77 | if (parentfd == -1) { | 59 | exit(1); |
78 | fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir); | ||
79 | exit(1); | ||
80 | } | ||
81 | // rootdir has to be owned by root and is not allowed to be generally writable, | ||
82 | // this also excludes /tmp, /var/tmp and such | ||
83 | struct stat s; | ||
84 | if (fstat(parentfd, &s) == -1) | ||
85 | errExit("fstat"); | ||
86 | if (s.st_uid != 0) { | ||
87 | fprintf(stderr, "Error: chroot directory should be owned by root\n"); | ||
88 | exit(1); | ||
89 | } | ||
90 | if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) { | ||
91 | fprintf(stderr, "Error: only root user should be given write permission on chroot directory\n"); | ||
92 | exit(1); | ||
93 | } | ||
94 | |||
95 | // check subdirectories in rootdir | ||
96 | fs_check_chroot_subdir("dev", parentfd, 0); | ||
97 | fs_check_chroot_subdir("etc", parentfd, 1); | ||
98 | fs_check_chroot_subdir("proc", parentfd, 0); | ||
99 | fs_check_chroot_subdir("tmp", parentfd, 0); | ||
100 | fs_check_chroot_subdir("var/tmp", parentfd, 0); | ||
101 | |||
102 | // there should be no checking on <chrootdir>/etc/resolv.conf | ||
103 | // the file is replaced with the real /etc/resolv.conf anyway | ||
104 | #if 0 | ||
105 | if (asprintf(&name, "%s/etc/resolv.conf", rootdir) == -1) | ||
106 | errExit("asprintf"); | ||
107 | if (stat(name, &s) == 0) { | ||
108 | if (s.st_uid != 0) { | ||
109 | fprintf(stderr, "Error: chroot /etc/resolv.conf should be owned by root\n"); | ||
110 | exit(1); | ||
111 | } | ||
112 | } | ||
113 | else { | ||
114 | fprintf(stderr, "Error: chroot /etc/resolv.conf not found\n"); | ||
115 | exit(1); | ||
116 | } | ||
117 | // on Arch /etc/resolv.conf could be a symlink to /run/systemd/resolve/resolv.conf | ||
118 | // on Ubuntu 17.04 /etc/resolv.conf could be a symlink to /run/resolveconf/resolv.conf | ||
119 | if (is_link(name)) { | ||
120 | // check the link points in chroot | ||
121 | char *rname = realpath(name, NULL); | ||
122 | if (!rname || strncmp(rname, rootdir, strlen(rootdir)) != 0) { | ||
123 | fprintf(stderr, "Error: chroot /etc/resolv.conf is pointing outside chroot\n"); | ||
124 | exit(1); | ||
125 | } | ||
126 | } | ||
127 | free(name); | ||
128 | #endif | ||
129 | |||
130 | // check x11 socket directory | ||
131 | if (getenv("FIREJAIL_X11")) | ||
132 | fs_check_chroot_subdir("tmp/.X11-unix", parentfd, 0); | ||
133 | |||
134 | close(parentfd); | ||
135 | } | 60 | } |
136 | 61 | ||
137 | // copy /etc/resolv.conf in chroot directory | 62 | // copy /etc/resolv.conf in chroot directory |
@@ -145,7 +70,7 @@ static void copy_resolvconf(int parentfd) { | |||
145 | if (fstat(in, &src) == -1) | 70 | if (fstat(in, &src) == -1) |
146 | errExit("fstat"); | 71 | errExit("fstat"); |
147 | // try to detect if resolv.conf has been bind mounted into the chroot | 72 | // try to detect if resolv.conf has been bind mounted into the chroot |
148 | // do nothing in this case in order to not truncate the real file | 73 | // do nothing in this case in order to not unlink the real file |
149 | struct stat dst; | 74 | struct stat dst; |
150 | if (fstatat(parentfd, "etc/resolv.conf", &dst, 0) == 0) { | 75 | if (fstatat(parentfd, "etc/resolv.conf", &dst, 0) == 0) { |
151 | if (src.st_dev == dst.st_dev && src.st_ino == dst.st_ino) { | 76 | if (src.st_dev == dst.st_dev && src.st_ino == dst.st_ino) { |
@@ -155,7 +80,8 @@ static void copy_resolvconf(int parentfd) { | |||
155 | } | 80 | } |
156 | if (arg_debug) | 81 | if (arg_debug) |
157 | printf("Updating /etc/resolv.conf in chroot\n"); | 82 | printf("Updating /etc/resolv.conf in chroot\n"); |
158 | int out = openat(parentfd, "etc/resolv.conf", O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); | 83 | unlinkat(parentfd, "etc/resolv.conf", 0); |
84 | int out = openat(parentfd, "etc/resolv.conf", O_CREAT|O_WRONLY|O_CLOEXEC, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); | ||
159 | if (out == -1) | 85 | if (out == -1) |
160 | errExit("open"); | 86 | errExit("open"); |
161 | if (sendfile(out, in, NULL, src.st_size) == -1) | 87 | if (sendfile(out, in, NULL, src.st_size) == -1) |
@@ -164,18 +90,65 @@ static void copy_resolvconf(int parentfd) { | |||
164 | close(out); | 90 | close(out); |
165 | } | 91 | } |
166 | 92 | ||
93 | // exit if error | ||
94 | static void check_subdir(int parentfd, const char *subdir, int check_writable) { | ||
95 | assert(subdir); | ||
96 | struct stat s; | ||
97 | if (fstatat(parentfd, subdir, &s, AT_SYMLINK_NOFOLLOW) != 0) { | ||
98 | fprintf(stderr, "Error: cannot find /%s in chroot directory\n", subdir); | ||
99 | exit(1); | ||
100 | } | ||
101 | if (!S_ISDIR(s.st_mode)) { | ||
102 | if (S_ISLNK(s.st_mode)) | ||
103 | fprintf(stderr, "Error: chroot /%s is a symbolic link\n", subdir); | ||
104 | else | ||
105 | fprintf(stderr, "Error: chroot /%s is not a directory\n", subdir); | ||
106 | exit(1); | ||
107 | } | ||
108 | if (s.st_uid != 0) { | ||
109 | fprintf(stderr, "Error: chroot /%s should owned by root\n", subdir); | ||
110 | exit(1); | ||
111 | } | ||
112 | if (check_writable && ((S_IWGRP|S_IWOTH) & s.st_mode) != 0) { | ||
113 | fprintf(stderr, "Error: only root user should be given write permission on chroot /%s\n", subdir); | ||
114 | exit(1); | ||
115 | } | ||
116 | } | ||
117 | |||
167 | // chroot into an existing directory; mount existing /dev and update /etc/resolv.conf | 118 | // chroot into an existing directory; mount existing /dev and update /etc/resolv.conf |
168 | void fs_chroot(const char *rootdir) { | 119 | void fs_chroot(const char *rootdir) { |
169 | assert(rootdir); | 120 | assert(rootdir); |
170 | 121 | ||
122 | // fails if there is any symlink or if rootdir is not a directory | ||
171 | int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 123 | int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
172 | if (parentfd == -1) | 124 | if (parentfd == -1) |
173 | errExit("safe_fd"); | 125 | errExit("safe_fd"); |
126 | // rootdir has to be owned by root and is not allowed to be generally writable, | ||
127 | // this also excludes /tmp and friends | ||
128 | struct stat s; | ||
129 | if (fstat(parentfd, &s) == -1) | ||
130 | errExit("fstat"); | ||
131 | if (s.st_uid != 0) { | ||
132 | fprintf(stderr, "Error: chroot directory should be owned by root\n"); | ||
133 | exit(1); | ||
134 | } | ||
135 | if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) { | ||
136 | fprintf(stderr, "Error: only root user should be given write permission on chroot directory\n"); | ||
137 | exit(1); | ||
138 | } | ||
139 | // check chroot subdirectories; /tmp/.X11-unix and /run are treated separately | ||
140 | check_subdir(parentfd, "dev", 0); | ||
141 | check_subdir(parentfd, "etc", 1); | ||
142 | check_subdir(parentfd, "proc", 0); | ||
143 | check_subdir(parentfd, "tmp", 0); | ||
144 | check_subdir(parentfd, "var/tmp", 0); | ||
174 | 145 | ||
175 | // mount-bind a /dev in rootdir | 146 | // mount-bind a /dev in rootdir |
176 | if (arg_debug) | 147 | if (arg_debug) |
177 | printf("Mounting /dev on chroot /dev\n"); | 148 | printf("Mounting /dev on chroot /dev\n"); |
178 | int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_CLOEXEC); | 149 | // open chroot /dev to get a file descriptor, |
150 | // then use this descriptor as a mount target | ||
151 | int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
179 | if (fd == -1) | 152 | if (fd == -1) |
180 | errExit("open"); | 153 | errExit("open"); |
181 | char *proc; | 154 | char *proc; |
@@ -189,7 +162,7 @@ void fs_chroot(const char *rootdir) { | |||
189 | // mount a brand new proc filesystem | 162 | // mount a brand new proc filesystem |
190 | if (arg_debug) | 163 | if (arg_debug) |
191 | printf("Mounting /proc filesystem on chroot /proc\n"); | 164 | printf("Mounting /proc filesystem on chroot /proc\n"); |
192 | fd = openat(parentfd, "proc", O_PATH|O_DIRECTORY|O_CLOEXEC); | 165 | fd = openat(parentfd, "proc", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
193 | if (fd == -1) | 166 | if (fd == -1) |
194 | errExit("open"); | 167 | errExit("open"); |
195 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 168 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) |
@@ -203,7 +176,8 @@ void fs_chroot(const char *rootdir) { | |||
203 | if (getenv("FIREJAIL_X11")) { | 176 | if (getenv("FIREJAIL_X11")) { |
204 | if (arg_debug) | 177 | if (arg_debug) |
205 | printf("Mounting /tmp/.X11-unix on chroot /tmp/.X11-unix\n"); | 178 | printf("Mounting /tmp/.X11-unix on chroot /tmp/.X11-unix\n"); |
206 | fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_CLOEXEC); | 179 | check_subdir(parentfd, "tmp/.X11-unix", 0); |
180 | fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
207 | if (fd == -1) | 181 | if (fd == -1) |
208 | errExit("open"); | 182 | errExit("open"); |
209 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 183 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) |
@@ -214,30 +188,22 @@ void fs_chroot(const char *rootdir) { | |||
214 | close(fd); | 188 | close(fd); |
215 | } | 189 | } |
216 | 190 | ||
217 | // update chroot resolv.conf | ||
218 | copy_resolvconf(parentfd); | ||
219 | |||
220 | // some older distros don't have a /run directory, create one by default | 191 | // some older distros don't have a /run directory, create one by default |
221 | struct stat s; | 192 | if (mkdirat(parentfd, "run", 0755) == -1 && errno != EEXIST) |
222 | if (fstatat(parentfd, "run", &s, AT_SYMLINK_NOFOLLOW) == 0) { | ||
223 | if (S_ISLNK(s.st_mode)) { | ||
224 | fprintf(stderr, "Error: chroot /run is a symbolic link\n"); | ||
225 | exit(1); | ||
226 | } | ||
227 | } | ||
228 | else if (mkdirat(parentfd, "run", 0755) == -1 && errno != EEXIST) | ||
229 | errExit("mkdir"); | 193 | errExit("mkdir"); |
230 | fs_check_chroot_subdir("run", parentfd, 1); | 194 | check_subdir(parentfd, "run", 1); |
231 | 195 | ||
232 | // create /run/firejail directory in chroot | 196 | // create /run/firejail directory in chroot |
233 | if (mkdirat(parentfd, RUN_FIREJAIL_DIR+1, 0755) == -1 && errno != EEXIST) | 197 | if (mkdirat(parentfd, RUN_FIREJAIL_DIR+1, 0755) == -1 && errno != EEXIST) |
234 | errExit("mkdir"); | 198 | errExit("mkdir"); |
199 | check_subdir(parentfd, RUN_FIREJAIL_DIR+1, 1); | ||
235 | 200 | ||
236 | // create /run/firejail/lib directory in chroot | 201 | // create /run/firejail/lib directory in chroot |
237 | if (mkdirat(parentfd, RUN_FIREJAIL_LIB_DIR+1, 0755) == -1 && errno != EEXIST) | 202 | if (mkdirat(parentfd, RUN_FIREJAIL_LIB_DIR+1, 0755) == -1 && errno != EEXIST) |
238 | errExit("mkdir"); | 203 | errExit("mkdir"); |
204 | check_subdir(parentfd, RUN_FIREJAIL_LIB_DIR+1, 1); | ||
239 | // mount lib directory into the chroot | 205 | // mount lib directory into the chroot |
240 | fd = openat(parentfd, RUN_FIREJAIL_LIB_DIR+1, O_PATH|O_DIRECTORY|O_CLOEXEC); | 206 | fd = openat(parentfd, RUN_FIREJAIL_LIB_DIR+1, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
241 | if (fd == -1) | 207 | if (fd == -1) |
242 | errExit("open"); | 208 | errExit("open"); |
243 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 209 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) |
@@ -250,8 +216,9 @@ void fs_chroot(const char *rootdir) { | |||
250 | // create /run/firejail/mnt directory in chroot | 216 | // create /run/firejail/mnt directory in chroot |
251 | if (mkdirat(parentfd, RUN_MNT_DIR+1, 0755) == -1 && errno != EEXIST) | 217 | if (mkdirat(parentfd, RUN_MNT_DIR+1, 0755) == -1 && errno != EEXIST) |
252 | errExit("mkdir"); | 218 | errExit("mkdir"); |
219 | check_subdir(parentfd, RUN_MNT_DIR+1, 1); | ||
253 | // mount the current mnt directory into the chroot | 220 | // mount the current mnt directory into the chroot |
254 | fd = openat(parentfd, RUN_MNT_DIR+1, O_PATH|O_DIRECTORY|O_CLOEXEC); | 221 | fd = openat(parentfd, RUN_MNT_DIR+1, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
255 | if (fd == -1) | 222 | if (fd == -1) |
256 | errExit("open"); | 223 | errExit("open"); |
257 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 224 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) |
@@ -261,6 +228,9 @@ void fs_chroot(const char *rootdir) { | |||
261 | free(proc); | 228 | free(proc); |
262 | close(fd); | 229 | close(fd); |
263 | 230 | ||
231 | // update chroot resolv.conf | ||
232 | copy_resolvconf(parentfd); | ||
233 | |||
264 | #ifdef HAVE_GCOV | 234 | #ifdef HAVE_GCOV |
265 | __gcov_flush(); | 235 | __gcov_flush(); |
266 | #endif | 236 | #endif |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 80cf71caf..487803770 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -398,8 +398,9 @@ void fs_private_cache(void); | |||
398 | void fs_mnt(const int enforce); | 398 | void fs_mnt(const int enforce); |
399 | 399 | ||
400 | // chroot.c | 400 | // chroot.c |
401 | // returns resolved chroot directory path | ||
402 | char *fs_check_chroot_dir(const char *rootdir); | ||
401 | // chroot into an existing directory; mount existing /dev and update /etc/resolv.conf | 403 | // chroot into an existing directory; mount existing /dev and update /etc/resolv.conf |
402 | void fs_check_chroot_dir(const char *rootdir); | ||
403 | void fs_chroot(const char *rootdir); | 404 | void fs_chroot(const char *rootdir); |
404 | 405 | ||
405 | // profile.c | 406 | // profile.c |
diff --git a/src/firejail/main.c b/src/firejail/main.c index e8664e914..5c83239ef 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -1659,35 +1659,21 @@ int main(int argc, char **argv) { | |||
1659 | fprintf(stderr, "Error: --chroot option is not available on Grsecurity systems\n"); | 1659 | fprintf(stderr, "Error: --chroot option is not available on Grsecurity systems\n"); |
1660 | exit(1); | 1660 | exit(1); |
1661 | } | 1661 | } |
1662 | 1662 | if (*(argv[i] + 9) == '\0') { | |
1663 | 1663 | fprintf(stderr, "Error: invalid chroot option\n"); | |
1664 | exit(1); | ||
1665 | } | ||
1664 | invalid_filename(argv[i] + 9, 0); // no globbing | 1666 | invalid_filename(argv[i] + 9, 0); // no globbing |
1665 | 1667 | ||
1666 | // extract chroot dirname | 1668 | // extract chroot dirname |
1667 | cfg.chrootdir = argv[i] + 9; | 1669 | char *tmp = argv[i] + 9; |
1668 | // if the directory starts with ~, expand the home directory | 1670 | // if the directory starts with ~, expand the home directory |
1669 | if (*cfg.chrootdir == '~') { | 1671 | if (*(argv[i] + 9) == '~') { |
1670 | char *tmp; | 1672 | if (asprintf(&tmp, "%s%s", cfg.homedir, argv[i] + 10) == -1) |
1671 | if (asprintf(&tmp, "%s%s", cfg.homedir, cfg.chrootdir + 1) == -1) | ||
1672 | errExit("asprintf"); | 1673 | errExit("asprintf"); |
1673 | cfg.chrootdir = tmp; | ||
1674 | } | 1674 | } |
1675 | 1675 | // check chroot directory | |
1676 | if (strstr(cfg.chrootdir, "..") || is_link(cfg.chrootdir)) { | 1676 | cfg.chrootdir = fs_check_chroot_dir(tmp); |
1677 | fprintf(stderr, "Error: invalid chroot directory %s\n", cfg.chrootdir); | ||
1678 | return 1; | ||
1679 | } | ||
1680 | |||
1681 | // check chroot dirname exists, don't allow "--chroot=/" | ||
1682 | char *rpath = realpath(cfg.chrootdir, NULL); | ||
1683 | if (rpath == NULL || strcmp(rpath, "/") == 0) { | ||
1684 | fprintf(stderr, "Error: invalid chroot directory\n"); | ||
1685 | exit(1); | ||
1686 | } | ||
1687 | cfg.chrootdir = rpath; | ||
1688 | |||
1689 | // check chroot directory structure | ||
1690 | fs_check_chroot_dir(cfg.chrootdir); | ||
1691 | } | 1677 | } |
1692 | else | 1678 | else |
1693 | exit_err_feature("chroot"); | 1679 | exit_err_feature("chroot"); |