diff options
Diffstat (limited to 'src/firejail/fs_home.c')
-rw-r--r-- | src/firejail/fs_home.c | 99 |
1 files changed, 62 insertions, 37 deletions
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index eab952eb8..1c4f9e07e 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -34,12 +34,13 @@ | |||
34 | #define O_PATH 010000000 | 34 | #define O_PATH 010000000 |
35 | #endif | 35 | #endif |
36 | 36 | ||
37 | static void skel(const char *homedir, uid_t u, gid_t g) { | 37 | static void skel(const char *homedir) { |
38 | char *fname; | 38 | EUID_ASSERT(); |
39 | 39 | ||
40 | // zsh | 40 | // zsh |
41 | if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) { | 41 | if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) { |
42 | // copy skel files | 42 | // copy skel files |
43 | char *fname; | ||
43 | if (asprintf(&fname, "%s/.zshrc", homedir) == -1) | 44 | if (asprintf(&fname, "%s/.zshrc", homedir) == -1) |
44 | errExit("asprintf"); | 45 | errExit("asprintf"); |
45 | // don't copy it if we already have the file | 46 | // don't copy it if we already have the file |
@@ -50,7 +51,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
50 | exit(1); | 51 | exit(1); |
51 | } | 52 | } |
52 | if (access("/etc/skel/.zshrc", R_OK) == 0) { | 53 | if (access("/etc/skel/.zshrc", R_OK) == 0) { |
53 | copy_file_as_user("/etc/skel/.zshrc", fname, u, g, 0644); // regular user | 54 | copy_file_as_user("/etc/skel/.zshrc", fname, 0644); // regular user |
54 | fs_logger("clone /etc/skel/.zshrc"); | 55 | fs_logger("clone /etc/skel/.zshrc"); |
55 | fs_logger2("clone", fname); | 56 | fs_logger2("clone", fname); |
56 | } | 57 | } |
@@ -64,6 +65,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
64 | // csh | 65 | // csh |
65 | else if (!arg_shell_none && strcmp(cfg.shell,"/bin/csh") == 0) { | 66 | else if (!arg_shell_none && strcmp(cfg.shell,"/bin/csh") == 0) { |
66 | // copy skel files | 67 | // copy skel files |
68 | char *fname; | ||
67 | if (asprintf(&fname, "%s/.cshrc", homedir) == -1) | 69 | if (asprintf(&fname, "%s/.cshrc", homedir) == -1) |
68 | errExit("asprintf"); | 70 | errExit("asprintf"); |
69 | // don't copy it if we already have the file | 71 | // don't copy it if we already have the file |
@@ -74,7 +76,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
74 | exit(1); | 76 | exit(1); |
75 | } | 77 | } |
76 | if (access("/etc/skel/.cshrc", R_OK) == 0) { | 78 | if (access("/etc/skel/.cshrc", R_OK) == 0) { |
77 | copy_file_as_user("/etc/skel/.cshrc", fname, u, g, 0644); // regular user | 79 | copy_file_as_user("/etc/skel/.cshrc", fname, 0644); // regular user |
78 | fs_logger("clone /etc/skel/.cshrc"); | 80 | fs_logger("clone /etc/skel/.cshrc"); |
79 | fs_logger2("clone", fname); | 81 | fs_logger2("clone", fname); |
80 | } | 82 | } |
@@ -88,6 +90,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
88 | // bash etc. | 90 | // bash etc. |
89 | else { | 91 | else { |
90 | // copy skel files | 92 | // copy skel files |
93 | char *fname; | ||
91 | if (asprintf(&fname, "%s/.bashrc", homedir) == -1) | 94 | if (asprintf(&fname, "%s/.bashrc", homedir) == -1) |
92 | errExit("asprintf"); | 95 | errExit("asprintf"); |
93 | // don't copy it if we already have the file | 96 | // don't copy it if we already have the file |
@@ -98,7 +101,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
98 | exit(1); | 101 | exit(1); |
99 | } | 102 | } |
100 | if (access("/etc/skel/.bashrc", R_OK) == 0) { | 103 | if (access("/etc/skel/.bashrc", R_OK) == 0) { |
101 | copy_file_as_user("/etc/skel/.bashrc", fname, u, g, 0644); // regular user | 104 | copy_file_as_user("/etc/skel/.bashrc", fname, 0644); // regular user |
102 | fs_logger("clone /etc/skel/.bashrc"); | 105 | fs_logger("clone /etc/skel/.bashrc"); |
103 | fs_logger2("clone", fname); | 106 | fs_logger2("clone", fname); |
104 | } | 107 | } |
@@ -108,6 +111,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) { | |||
108 | } | 111 | } |
109 | 112 | ||
110 | static int store_xauthority(void) { | 113 | static int store_xauthority(void) { |
114 | EUID_ASSERT(); | ||
111 | if (arg_x11_block) | 115 | if (arg_x11_block) |
112 | return 0; | 116 | return 0; |
113 | 117 | ||
@@ -118,7 +122,7 @@ static int store_xauthority(void) { | |||
118 | errExit("asprintf"); | 122 | errExit("asprintf"); |
119 | 123 | ||
120 | struct stat s; | 124 | struct stat s; |
121 | if (lstat_as_user(src, &s) == 0) { | 125 | if (lstat(src, &s) == 0) { |
122 | if (S_ISLNK(s.st_mode)) { | 126 | if (S_ISLNK(s.st_mode)) { |
123 | fwarning("invalid .Xauthority file\n"); | 127 | fwarning("invalid .Xauthority file\n"); |
124 | free(src); | 128 | free(src); |
@@ -126,6 +130,7 @@ static int store_xauthority(void) { | |||
126 | } | 130 | } |
127 | 131 | ||
128 | // create an empty file as root, and change ownership to user | 132 | // create an empty file as root, and change ownership to user |
133 | EUID_ROOT(); | ||
129 | FILE *fp = fopen(dest, "we"); | 134 | FILE *fp = fopen(dest, "we"); |
130 | if (fp) { | 135 | if (fp) { |
131 | fprintf(fp, "\n"); | 136 | fprintf(fp, "\n"); |
@@ -134,10 +139,11 @@ static int store_xauthority(void) { | |||
134 | } | 139 | } |
135 | else | 140 | else |
136 | errExit("fopen"); | 141 | errExit("fopen"); |
142 | EUID_USER(); | ||
137 | 143 | ||
138 | copy_file_as_user(src, dest, getuid(), getgid(), 0600); // regular user | 144 | copy_file_as_user(src, dest, 0600); // regular user |
139 | fs_logger2("clone", dest); | ||
140 | selinux_relabel_path(dest, src); | 145 | selinux_relabel_path(dest, src); |
146 | fs_logger2("clone", dest); | ||
141 | free(src); | 147 | free(src); |
142 | return 1; // file copied | 148 | return 1; // file copied |
143 | } | 149 | } |
@@ -147,6 +153,7 @@ static int store_xauthority(void) { | |||
147 | } | 153 | } |
148 | 154 | ||
149 | static int store_asoundrc(void) { | 155 | static int store_asoundrc(void) { |
156 | EUID_ASSERT(); | ||
150 | if (arg_nosound) | 157 | if (arg_nosound) |
151 | return 0; | 158 | return 0; |
152 | 159 | ||
@@ -157,11 +164,11 @@ static int store_asoundrc(void) { | |||
157 | errExit("asprintf"); | 164 | errExit("asprintf"); |
158 | 165 | ||
159 | struct stat s; | 166 | struct stat s; |
160 | if (lstat_as_user(src, &s) == 0) { | 167 | if (lstat(src, &s) == 0) { |
161 | if (S_ISLNK(s.st_mode)) { | 168 | if (S_ISLNK(s.st_mode)) { |
162 | // make sure the real path of the file is inside the home directory | 169 | // make sure the real path of the file is inside the home directory |
163 | /* coverity[toctou] */ | 170 | /* coverity[toctou] */ |
164 | char *rp = realpath_as_user(src); | 171 | char *rp = realpath(src, NULL); |
165 | if (!rp) { | 172 | if (!rp) { |
166 | fprintf(stderr, "Error: Cannot access %s\n", src); | 173 | fprintf(stderr, "Error: Cannot access %s\n", src); |
167 | exit(1); | 174 | exit(1); |
@@ -174,6 +181,7 @@ static int store_asoundrc(void) { | |||
174 | } | 181 | } |
175 | 182 | ||
176 | // create an empty file as root, and change ownership to user | 183 | // create an empty file as root, and change ownership to user |
184 | EUID_ROOT(); | ||
177 | FILE *fp = fopen(dest, "we"); | 185 | FILE *fp = fopen(dest, "we"); |
178 | if (fp) { | 186 | if (fp) { |
179 | fprintf(fp, "\n"); | 187 | fprintf(fp, "\n"); |
@@ -182,10 +190,11 @@ static int store_asoundrc(void) { | |||
182 | } | 190 | } |
183 | else | 191 | else |
184 | errExit("fopen"); | 192 | errExit("fopen"); |
193 | EUID_USER(); | ||
185 | 194 | ||
186 | copy_file_as_user(src, dest, getuid(), getgid(), 0644); // regular user | 195 | copy_file_as_user(src, dest, 0644); // regular user |
187 | selinux_relabel_path(dest, src); | ||
188 | fs_logger2("clone", dest); | 196 | fs_logger2("clone", dest); |
197 | selinux_relabel_path(dest, src); | ||
189 | free(src); | 198 | free(src); |
190 | return 1; // file copied | 199 | return 1; // file copied |
191 | } | 200 | } |
@@ -195,6 +204,7 @@ static int store_asoundrc(void) { | |||
195 | } | 204 | } |
196 | 205 | ||
197 | static void copy_xauthority(void) { | 206 | static void copy_xauthority(void) { |
207 | EUID_ASSERT(); | ||
198 | // copy XAUTHORITY_FILE in the new home directory | 208 | // copy XAUTHORITY_FILE in the new home directory |
199 | char *src = RUN_XAUTHORITY_FILE ; | 209 | char *src = RUN_XAUTHORITY_FILE ; |
200 | char *dest; | 210 | char *dest; |
@@ -207,16 +217,18 @@ static void copy_xauthority(void) { | |||
207 | exit(1); | 217 | exit(1); |
208 | } | 218 | } |
209 | 219 | ||
210 | copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user | 220 | copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user |
211 | selinux_relabel_path(dest, src); | ||
212 | fs_logger2("clone", dest); | 221 | fs_logger2("clone", dest); |
222 | selinux_relabel_path(dest, dest); | ||
213 | free(dest); | 223 | free(dest); |
214 | 224 | ||
215 | // delete the temporary file | 225 | EUID_ROOT(); |
216 | unlink(src); | 226 | unlink(src); // delete the temporary file |
227 | EUID_USER(); | ||
217 | } | 228 | } |
218 | 229 | ||
219 | static void copy_asoundrc(void) { | 230 | static void copy_asoundrc(void) { |
231 | EUID_ASSERT(); | ||
220 | // copy ASOUNDRC_FILE in the new home directory | 232 | // copy ASOUNDRC_FILE in the new home directory |
221 | char *src = RUN_ASOUNDRC_FILE ; | 233 | char *src = RUN_ASOUNDRC_FILE ; |
222 | char *dest; | 234 | char *dest; |
@@ -229,13 +241,14 @@ static void copy_asoundrc(void) { | |||
229 | exit(1); | 241 | exit(1); |
230 | } | 242 | } |
231 | 243 | ||
232 | copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user | 244 | copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user |
233 | selinux_relabel_path(dest, src); | ||
234 | fs_logger2("clone", dest); | 245 | fs_logger2("clone", dest); |
246 | selinux_relabel_path(dest, dest); | ||
235 | free(dest); | 247 | free(dest); |
236 | 248 | ||
237 | // delete the temporary file | 249 | EUID_ROOT(); |
238 | unlink(src); | 250 | unlink(src); // delete the temporary file |
251 | EUID_USER(); | ||
239 | } | 252 | } |
240 | 253 | ||
241 | // private mode (--private=homedir): | 254 | // private mode (--private=homedir): |
@@ -248,18 +261,18 @@ void fs_private_homedir(void) { | |||
248 | char *private_homedir = cfg.home_private; | 261 | char *private_homedir = cfg.home_private; |
249 | assert(homedir); | 262 | assert(homedir); |
250 | assert(private_homedir); | 263 | assert(private_homedir); |
264 | EUID_ASSERT(); | ||
265 | |||
266 | uid_t u = getuid(); | ||
267 | // gid_t g = getgid(); | ||
251 | 268 | ||
252 | int xflag = store_xauthority(); | 269 | int xflag = store_xauthority(); |
253 | int aflag = store_asoundrc(); | 270 | int aflag = store_asoundrc(); |
254 | 271 | ||
255 | uid_t u = getuid(); | ||
256 | gid_t g = getgid(); | ||
257 | |||
258 | // mount bind private_homedir on top of homedir | 272 | // mount bind private_homedir on top of homedir |
259 | if (arg_debug) | 273 | if (arg_debug) |
260 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); | 274 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); |
261 | // get file descriptors for homedir and private_homedir, fails if there is any symlink | 275 | // get file descriptors for homedir and private_homedir, fails if there is any symlink |
262 | EUID_USER(); | ||
263 | int src = safer_openat(-1, private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 276 | int src = safer_openat(-1, private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
264 | if (src == -1) | 277 | if (src == -1) |
265 | errExit("opening private directory"); | 278 | errExit("opening private directory"); |
@@ -287,6 +300,7 @@ void fs_private_homedir(void) { | |||
287 | EUID_ROOT(); | 300 | EUID_ROOT(); |
288 | if (bind_mount_by_fd(src, dst)) | 301 | if (bind_mount_by_fd(src, dst)) |
289 | errExit("mount bind"); | 302 | errExit("mount bind"); |
303 | EUID_USER(); | ||
290 | 304 | ||
291 | // check /proc/self/mountinfo to confirm the mount is ok | 305 | // check /proc/self/mountinfo to confirm the mount is ok |
292 | MountData *mptr = get_last_mount(); | 306 | MountData *mptr = get_last_mount(); |
@@ -305,6 +319,7 @@ void fs_private_homedir(void) { | |||
305 | // if (chmod(homedir, s.st_mode) == -1) | 319 | // if (chmod(homedir, s.st_mode) == -1) |
306 | // errExit("mount-bind chmod"); | 320 | // errExit("mount-bind chmod"); |
307 | 321 | ||
322 | EUID_ROOT(); | ||
308 | if (u != 0) { | 323 | if (u != 0) { |
309 | // mask /root | 324 | // mask /root |
310 | if (arg_debug) | 325 | if (arg_debug) |
@@ -323,8 +338,9 @@ void fs_private_homedir(void) { | |||
323 | selinux_relabel_path("/home", "/home"); | 338 | selinux_relabel_path("/home", "/home"); |
324 | fs_logger("tmpfs /home"); | 339 | fs_logger("tmpfs /home"); |
325 | } | 340 | } |
341 | EUID_USER(); | ||
326 | 342 | ||
327 | skel(homedir, u, g); | 343 | skel(homedir); |
328 | if (xflag) | 344 | if (xflag) |
329 | copy_xauthority(); | 345 | copy_xauthority(); |
330 | if (aflag) | 346 | if (aflag) |
@@ -339,12 +355,15 @@ void fs_private_homedir(void) { | |||
339 | void fs_private(void) { | 355 | void fs_private(void) { |
340 | char *homedir = cfg.homedir; | 356 | char *homedir = cfg.homedir; |
341 | assert(homedir); | 357 | assert(homedir); |
358 | EUID_ASSERT(); | ||
359 | |||
342 | uid_t u = getuid(); | 360 | uid_t u = getuid(); |
343 | gid_t g = getgid(); | 361 | gid_t g = getgid(); |
344 | 362 | ||
345 | int xflag = store_xauthority(); | 363 | int xflag = store_xauthority(); |
346 | int aflag = store_asoundrc(); | 364 | int aflag = store_asoundrc(); |
347 | 365 | ||
366 | EUID_ROOT(); | ||
348 | // mask /root | 367 | // mask /root |
349 | if (arg_debug) | 368 | if (arg_debug) |
350 | printf("Mounting a new /root directory\n"); | 369 | printf("Mounting a new /root directory\n"); |
@@ -387,8 +406,9 @@ void fs_private(void) { | |||
387 | 406 | ||
388 | selinux_relabel_path(homedir, homedir); | 407 | selinux_relabel_path(homedir, homedir); |
389 | } | 408 | } |
409 | EUID_USER(); | ||
390 | 410 | ||
391 | skel(homedir, u, g); | 411 | skel(homedir); |
392 | if (xflag) | 412 | if (xflag) |
393 | copy_xauthority(); | 413 | copy_xauthority(); |
394 | if (aflag) | 414 | if (aflag) |
@@ -530,26 +550,29 @@ static void duplicate(char *name) { | |||
530 | // set skel files, | 550 | // set skel files, |
531 | // restore .Xauthority | 551 | // restore .Xauthority |
532 | void fs_private_home_list(void) { | 552 | void fs_private_home_list(void) { |
533 | timetrace_start(); | ||
534 | |||
535 | char *homedir = cfg.homedir; | 553 | char *homedir = cfg.homedir; |
536 | char *private_list = cfg.home_private_keep; | 554 | char *private_list = cfg.home_private_keep; |
537 | assert(homedir); | 555 | assert(homedir); |
538 | assert(private_list); | 556 | assert(private_list); |
557 | EUID_ASSERT(); | ||
539 | 558 | ||
540 | int xflag = store_xauthority(); | 559 | timetrace_start(); |
541 | int aflag = store_asoundrc(); | ||
542 | 560 | ||
543 | uid_t uid = getuid(); | 561 | uid_t uid = getuid(); |
544 | gid_t gid = getgid(); | 562 | gid_t gid = getgid(); |
545 | 563 | ||
564 | int xflag = store_xauthority(); | ||
565 | int aflag = store_asoundrc(); | ||
566 | |||
546 | // create /run/firejail/mnt/home directory | 567 | // create /run/firejail/mnt/home directory |
568 | EUID_ROOT(); | ||
547 | mkdir_attr(RUN_HOME_DIR, 0755, uid, gid); | 569 | mkdir_attr(RUN_HOME_DIR, 0755, uid, gid); |
548 | selinux_relabel_path(RUN_HOME_DIR, homedir); | 570 | selinux_relabel_path(RUN_HOME_DIR, homedir); |
571 | |||
549 | fs_logger_print(); // save the current log | 572 | fs_logger_print(); // save the current log |
573 | EUID_USER(); | ||
550 | 574 | ||
551 | // copy the list of files in the new home directory | 575 | // copy the list of files in the new home directory |
552 | EUID_USER(); | ||
553 | if (arg_debug) | 576 | if (arg_debug) |
554 | printf("Copying files in the new home:\n"); | 577 | printf("Copying files in the new home:\n"); |
555 | char *dlist = strdup(cfg.home_private_keep); | 578 | char *dlist = strdup(cfg.home_private_keep); |
@@ -588,6 +611,7 @@ void fs_private_home_list(void) { | |||
588 | if (bind_mount_path_to_fd(RUN_HOME_DIR, fd)) | 611 | if (bind_mount_path_to_fd(RUN_HOME_DIR, fd)) |
589 | errExit("mount bind"); | 612 | errExit("mount bind"); |
590 | close(fd); | 613 | close(fd); |
614 | EUID_USER(); | ||
591 | 615 | ||
592 | // check /proc/self/mountinfo to confirm the mount is ok | 616 | // check /proc/self/mountinfo to confirm the mount is ok |
593 | MountData *mptr = get_last_mount(); | 617 | MountData *mptr = get_last_mount(); |
@@ -595,11 +619,7 @@ void fs_private_home_list(void) { | |||
595 | errLogExit("invalid private-home mount"); | 619 | errLogExit("invalid private-home mount"); |
596 | fs_logger2("tmpfs", homedir); | 620 | fs_logger2("tmpfs", homedir); |
597 | 621 | ||
598 | // mask RUN_HOME_DIR, it is writable and not noexec | 622 | EUID_ROOT(); |
599 | if (mount("tmpfs", RUN_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
600 | errExit("mounting tmpfs"); | ||
601 | fs_logger2("tmpfs", RUN_HOME_DIR); | ||
602 | |||
603 | if (uid != 0) { | 623 | if (uid != 0) { |
604 | // mask /root | 624 | // mask /root |
605 | if (arg_debug) | 625 | if (arg_debug) |
@@ -619,7 +639,12 @@ void fs_private_home_list(void) { | |||
619 | fs_logger("tmpfs /home"); | 639 | fs_logger("tmpfs /home"); |
620 | } | 640 | } |
621 | 641 | ||
622 | skel(homedir, uid, gid); | 642 | // mask RUN_HOME_DIR, it is writable and not noexec |
643 | if (mount("tmpfs", RUN_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
644 | errExit("mounting tmpfs"); | ||
645 | EUID_USER(); | ||
646 | |||
647 | skel(homedir); | ||
623 | if (xflag) | 648 | if (xflag) |
624 | copy_xauthority(); | 649 | copy_xauthority(); |
625 | if (aflag) | 650 | if (aflag) |