diff options
Diffstat (limited to 'src/firejail/fs_etc.c')
-rw-r--r-- | src/firejail/fs_etc.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c new file mode 100644 index 000000000..57b0b4f3e --- /dev/null +++ b/src/firejail/fs_etc.c | |||
@@ -0,0 +1,146 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | #include "firejail.h" | ||
21 | #include <sys/mount.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <sys/types.h> | ||
24 | #include <sys/wait.h> | ||
25 | #include <unistd.h> | ||
26 | |||
27 | static void check_dir_or_file(const char *name) { | ||
28 | assert(name); | ||
29 | struct stat s; | ||
30 | char *fname; | ||
31 | if (asprintf(&fname, "/etc/%s", name) == -1) | ||
32 | errExit("asprintf"); | ||
33 | if (arg_debug) | ||
34 | printf("Checking %s\n", fname); | ||
35 | if (stat(fname, &s) == -1) { | ||
36 | fprintf(stderr, "Error: file %s not found.\n", fname); | ||
37 | exit(1); | ||
38 | } | ||
39 | |||
40 | // dir or regular file | ||
41 | if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { | ||
42 | free(fname); | ||
43 | return; | ||
44 | } | ||
45 | |||
46 | if (!is_link(fname)) { | ||
47 | free(fname); | ||
48 | return; | ||
49 | } | ||
50 | |||
51 | fprintf(stderr, "Error: invalid file type, %s.\n", fname); | ||
52 | exit(1); | ||
53 | } | ||
54 | |||
55 | void fs_check_etc_list(void) { | ||
56 | if (strstr(cfg.etc_private_keep, "..")) { | ||
57 | fprintf(stderr, "Error: invalid private etc list\n"); | ||
58 | exit(1); | ||
59 | } | ||
60 | |||
61 | char *dlist = strdup(cfg.etc_private_keep); | ||
62 | if (!dlist) | ||
63 | errExit("strdup"); | ||
64 | |||
65 | char *ptr = strtok(dlist, ","); | ||
66 | check_dir_or_file(ptr); | ||
67 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
68 | check_dir_or_file(ptr); | ||
69 | |||
70 | free(dlist); | ||
71 | } | ||
72 | |||
73 | static void duplicate(char *fname) { | ||
74 | char *cmd; | ||
75 | |||
76 | // copy the file | ||
77 | if (asprintf(&cmd, "cp -a --parents /etc/%s %s", fname, MNT_DIR) == -1) | ||
78 | errExit("asprintf"); | ||
79 | if (arg_debug) | ||
80 | printf("%s\n", cmd); | ||
81 | if (system(cmd)) | ||
82 | errExit("system cp -a --parents"); | ||
83 | free(cmd); | ||
84 | } | ||
85 | |||
86 | |||
87 | void fs_private_etc_list(void) { | ||
88 | char *private_list = cfg.etc_private_keep; | ||
89 | assert(private_list); | ||
90 | |||
91 | uid_t u = getuid(); | ||
92 | gid_t g = getgid(); | ||
93 | struct stat s; | ||
94 | if (stat("/etc", &s) == -1) { | ||
95 | fprintf(stderr, "Error: cannot find user /etc directory\n"); | ||
96 | exit(1); | ||
97 | } | ||
98 | |||
99 | // create /tmp/firejail/mnt/etc directory | ||
100 | fs_build_mnt_dir(); | ||
101 | int rv = mkdir(ETC_DIR, S_IRWXU | S_IRWXG | S_IRWXO); | ||
102 | if (rv == -1) | ||
103 | errExit("mkdir"); | ||
104 | if (chown(ETC_DIR, 0, 0) < 0) | ||
105 | errExit("chown"); | ||
106 | if (chmod(ETC_DIR, 0755) < 0) | ||
107 | errExit("chmod"); | ||
108 | |||
109 | // copy the list of files in the new etc directory | ||
110 | // using a new child process without root privileges | ||
111 | pid_t child = fork(); | ||
112 | if (child < 0) | ||
113 | errExit("fork"); | ||
114 | if (child == 0) { | ||
115 | if (arg_debug) | ||
116 | printf("Copying files in the new home:\n"); | ||
117 | |||
118 | // elevate privileges - files in the new /etc directory belong to root | ||
119 | if (setreuid(0, 0) < 0) | ||
120 | errExit("setreuid"); | ||
121 | if (setregid(0, 0) < 0) | ||
122 | errExit("setregid"); | ||
123 | |||
124 | // copy the list of files in the new home directory | ||
125 | char *dlist = strdup(private_list); | ||
126 | if (!dlist) | ||
127 | errExit("strdup"); | ||
128 | |||
129 | char *ptr = strtok(dlist, ","); | ||
130 | duplicate(ptr); | ||
131 | |||
132 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
133 | duplicate(ptr); | ||
134 | free(dlist); | ||
135 | exit(0); | ||
136 | } | ||
137 | // wait for the child to finish | ||
138 | waitpid(child, NULL, 0); | ||
139 | |||
140 | if (arg_debug) | ||
141 | printf("Mount-bind %s on top of /etc\n", ETC_DIR); | ||
142 | if (mount(ETC_DIR, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) | ||
143 | errExit("mount bind"); | ||
144 | |||
145 | } | ||
146 | |||