diff options
author | smitsohu <smitsohu@gmail.com> | 2018-10-25 22:07:19 +0200 |
---|---|---|
committer | smitsohu <smitsohu@gmail.com> | 2018-10-25 22:07:19 +0200 |
commit | a7164cb38171c0291fac7f3a0767fe8f1c69b02d (patch) | |
tree | 88335663b94f807ebe7efcdb70eaeae80701601e /src/firejail/mountinfo.c | |
parent | merges (diff) | |
download | firejail-a7164cb38171c0291fac7f3a0767fe8f1c69b02d.tar.gz firejail-a7164cb38171c0291fac7f3a0767fe8f1c69b02d.tar.zst firejail-a7164cb38171c0291fac7f3a0767fe8f1c69b02d.zip |
experimental: remounts child mount points as well (read-only, read-write, noexec)
Diffstat (limited to 'src/firejail/mountinfo.c')
-rw-r--r-- | src/firejail/mountinfo.c | 205 |
1 files changed, 169 insertions, 36 deletions
diff --git a/src/firejail/mountinfo.c b/src/firejail/mountinfo.c index 4a7816901..b7760ba67 100644 --- a/src/firejail/mountinfo.c +++ b/src/firejail/mountinfo.c | |||
@@ -19,16 +19,19 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include "firejail.h" | 21 | #include "firejail.h" |
22 | #include <fcntl.h> | ||
22 | 23 | ||
23 | #define MAX_BUF 4096 | 24 | #define MAX_BUF 4096 |
25 | |||
24 | static char mbuf[MAX_BUF]; | 26 | static char mbuf[MAX_BUF]; |
25 | static MountData mdata; | 27 | static MountData mdata; |
26 | 28 | ||
29 | |||
27 | // Convert octal escape sequence to decimal value | 30 | // Convert octal escape sequence to decimal value |
28 | static int read_oct(const char *path) { | 31 | static int read_oct(const char *path) { |
29 | int decimal = 0; | 32 | int decimal = 0; |
30 | int digit, i; | 33 | int digit, i; |
31 | // there are always three octal digits | 34 | // there are always exactly three octal digits |
32 | for (i = 1; i < 4; i++) { | 35 | for (i = 1; i < 4; i++) { |
33 | digit = *(path + i); | 36 | digit = *(path + i); |
34 | if (digit < '0' || digit > '7') { | 37 | if (digit < '0' || digit > '7') { |
@@ -61,43 +64,38 @@ static void unmangle_path(char *path) { | |||
61 | } | 64 | } |
62 | } | 65 | } |
63 | 66 | ||
64 | // Get info regarding the last kernel mount operation. | 67 | // Parse a line from /proc/self/mountinfo, |
65 | // The return value points to a static area, and will be overwritten by subsequent calls. | 68 | // the function does an exit(1) if anything goes wrong. |
66 | // The function does an exit(1) if anything goes wrong. | 69 | static void parse_line(char *line, MountData *output) { |
67 | MountData *get_last_mount(void) { | 70 | assert(line && *line); |
68 | // open /proc/self/mountinfo | 71 | memset(output, 0, sizeof(*output)); |
69 | FILE *fp = fopen("/proc/self/mountinfo", "r"); | 72 | // extract filesystem name, directory and filesystem types |
70 | if (!fp) | ||
71 | goto errexit; | ||
72 | |||
73 | mbuf[0] = '\0'; | ||
74 | while (fgets(mbuf, MAX_BUF, fp)); | ||
75 | fclose(fp); | ||
76 | if (arg_debug) | ||
77 | printf("%s", mbuf); | ||
78 | |||
79 | // extract filesystem name, directory and filesystem type | ||
80 | // examples: | 73 | // examples: |
81 | // 587 543 8:1 /tmp /etc rw,relatime master:1 - ext4 /dev/sda1 rw,errors=remount-ro,data=ordered | 74 | // 587 543 8:1 /tmp /etc rw,relatime master:1 - ext4 /dev/sda1 rw,errors=remount-ro,data=ordered |
82 | // mdata.fsname: /tmp | 75 | // output.mountid: 587 |
83 | // mdata.dir: /etc | 76 | // output.fsname: /tmp |
84 | // mdata.fstype: ext4 | 77 | // output.dir: /etc |
78 | // output.fstype: ext4 | ||
85 | // 585 564 0:76 / /home/netblue/.cache rw,nosuid,nodev - tmpfs tmpfs rw | 79 | // 585 564 0:76 / /home/netblue/.cache rw,nosuid,nodev - tmpfs tmpfs rw |
86 | // mdata.fsname: / | 80 | // output.mountid: 585 |
87 | // mdata.dir: /home/netblue/.cache | 81 | // output.fsname: / |
88 | // mdata.fstype: tmpfs | 82 | // output.dir: /home/netblue/.cache |
89 | memset(&mdata, 0, sizeof(mdata)); | 83 | // output.fstype: tmpfs |
90 | char *ptr = strtok(mbuf, " "); | 84 | |
85 | char *ptr = strtok(line, " "); | ||
91 | if (!ptr) | 86 | if (!ptr) |
92 | goto errexit; | 87 | goto errexit; |
93 | 88 | if (ptr != line) | |
89 | goto errexit; | ||
90 | output->mountid = atoi(ptr); | ||
94 | int cnt = 1; | 91 | int cnt = 1; |
92 | |||
95 | while ((ptr = strtok(NULL, " ")) != NULL) { | 93 | while ((ptr = strtok(NULL, " ")) != NULL) { |
96 | cnt++; | 94 | cnt++; |
97 | if (cnt == 4) | 95 | if (cnt == 4) |
98 | mdata.fsname = ptr; | 96 | output->fsname = ptr; |
99 | else if (cnt == 5) { | 97 | else if (cnt == 5) { |
100 | mdata.dir = ptr; | 98 | output->dir = ptr; |
101 | break; | 99 | break; |
102 | } | 100 | } |
103 | } | 101 | } |
@@ -109,21 +107,156 @@ MountData *get_last_mount(void) { | |||
109 | ptr = strtok(NULL, " "); | 107 | ptr = strtok(NULL, " "); |
110 | if (!ptr) | 108 | if (!ptr) |
111 | goto errexit; | 109 | goto errexit; |
112 | mdata.fstype = ptr++; | 110 | output->fstype = ptr++; |
113 | 111 | ||
114 | if (mdata.fsname == NULL || | 112 | |
115 | mdata.dir == NULL || | 113 | if (output->mountid == 0 || |
116 | mdata.fstype == NULL) | 114 | output->fsname == NULL || |
115 | output->dir == NULL || | ||
116 | output->fstype == NULL) | ||
117 | goto errexit; | 117 | goto errexit; |
118 | 118 | ||
119 | unmangle_path(mdata.fsname); | 119 | // restore empty spaces |
120 | unmangle_path(mdata.dir); | 120 | unmangle_path(output->fsname); |
121 | unmangle_path(output->dir); | ||
122 | |||
123 | return; | ||
124 | |||
125 | errexit: | ||
126 | fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n"); | ||
127 | exit(1); | ||
128 | } | ||
129 | |||
130 | // The return value points to a static area, and will be overwritten by subsequent calls. | ||
131 | MountData *get_last_mount(void) { | ||
132 | // open /proc/self/mountinfo | ||
133 | FILE *fp = fopen("/proc/self/mountinfo", "re"); | ||
134 | if (!fp) { | ||
135 | perror("fopen"); | ||
136 | fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n"); | ||
137 | exit(1); | ||
138 | } | ||
139 | |||
140 | mbuf[0] = '\0'; | ||
141 | // go to the last line | ||
142 | while (fgets(mbuf, MAX_BUF, fp)); | ||
143 | fclose(fp); | ||
144 | if (arg_debug) | ||
145 | printf("%s", mbuf); | ||
146 | |||
147 | parse_line(mbuf, &mdata); | ||
121 | 148 | ||
122 | if (arg_debug) | 149 | if (arg_debug) |
123 | printf("fsname=%s dir=%s fstype=%s\n", mdata.fsname, mdata.dir, mdata.fstype); | 150 | printf("mountid=%d fsname=%s dir=%s fstype=%s\n", mdata.mountid, mdata.fsname, mdata.dir, mdata.fstype); |
124 | return &mdata; | 151 | return &mdata; |
152 | } | ||
153 | |||
154 | // Extract the mount id from /proc/self/fdinfo and return it. | ||
155 | int get_mount_id(const char *path) { | ||
156 | EUID_ASSERT(); | ||
157 | int fd = open(path, O_PATH|O_CLOEXEC); | ||
158 | if (fd == -1) | ||
159 | return 0; | ||
160 | |||
161 | char *fdinfo; | ||
162 | if (asprintf(&fdinfo, "/proc/self/fdinfo/%d", fd) == -1) | ||
163 | errExit("asprintf"); | ||
164 | EUID_ROOT(); | ||
165 | FILE *fp = fopen(fdinfo, "re"); | ||
166 | EUID_USER(); | ||
167 | if (!fp) | ||
168 | goto errexit; | ||
169 | // go to the last line | ||
170 | char buf[MAX_BUF]; | ||
171 | while (fgets(buf, MAX_BUF, fp)); | ||
172 | fclose(fp); | ||
173 | close(fd); | ||
174 | // go to the mount id | ||
175 | if (strncmp(buf, "mnt_id:", 7) != 0) | ||
176 | goto errexit; | ||
177 | char *ptr = buf + 7; | ||
178 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { | ||
179 | ptr++; | ||
180 | } | ||
181 | if (*ptr == '\0') | ||
182 | goto errexit; | ||
183 | free(fdinfo); | ||
184 | |||
185 | return atoi(ptr); | ||
125 | 186 | ||
126 | errexit: | 187 | errexit: |
127 | fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n"); | 188 | fprintf(stderr, "Error: cannot read file in /proc/self/fdinfo\n"); |
128 | exit(1); | 189 | exit(1); |
129 | } | 190 | } |
191 | |||
192 | // Return array with all paths that might need a remount. | ||
193 | char **get_all_mounts(const int mountid, const char *path) { | ||
194 | // open /proc/self/mountinfo | ||
195 | FILE *fp = fopen("/proc/self/mountinfo", "re"); | ||
196 | if (!fp) { | ||
197 | perror("fopen"); | ||
198 | fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n"); | ||
199 | exit(1); | ||
200 | } | ||
201 | |||
202 | size_t size = 32; | ||
203 | size_t cnt = 0; | ||
204 | char **rv = malloc(size * sizeof(*rv)); | ||
205 | if (!rv) | ||
206 | errExit("malloc"); | ||
207 | |||
208 | // read /proc/self/mountinfo | ||
209 | size_t pathlen = strlen(path); | ||
210 | int found = 0; | ||
211 | while (fgets(mbuf, MAX_BUF, fp)) { | ||
212 | // find mount point with mount id | ||
213 | if (!found) { | ||
214 | parse_line(mbuf, &mdata); | ||
215 | if (mdata.mountid == mountid) { | ||
216 | // don't remount blacklisted paths, | ||
217 | // give up if mount id has been reassigned | ||
218 | if (strstr(mdata.fsname, "firejail.ro.dir") || | ||
219 | strstr(mdata.fsname, "firejail.ro.file") || | ||
220 | strncmp(mdata.dir, path, strlen(mdata.dir))) | ||
221 | break; | ||
222 | |||
223 | *rv = strdup(path); | ||
224 | if (*rv == NULL) | ||
225 | errExit("strdup"); | ||
226 | cnt++; | ||
227 | found = 1; | ||
228 | continue; | ||
229 | } | ||
230 | else | ||
231 | continue; | ||
232 | } | ||
233 | // from here on add all mount points below path | ||
234 | parse_line(mbuf, &mdata); | ||
235 | if (strncmp(mdata.dir, path, pathlen) == 0 && | ||
236 | mdata.dir[pathlen] == '/' && | ||
237 | strstr(mdata.fsname, "firejail.ro.dir") == NULL && | ||
238 | strstr(mdata.fsname, "firejail.ro.file") == NULL) { | ||
239 | |||
240 | if (cnt >= size) { | ||
241 | size *= 2; | ||
242 | rv = realloc(rv, size * sizeof(*rv)); | ||
243 | if (!rv) | ||
244 | errExit("realloc"); | ||
245 | } | ||
246 | rv[cnt] = strdup(mdata.dir); | ||
247 | if (!rv[cnt]) | ||
248 | errExit("strdup"); | ||
249 | cnt++; | ||
250 | } | ||
251 | } | ||
252 | if (cnt == size) { | ||
253 | size++; | ||
254 | rv = realloc(rv, size * sizeof(*rv)); | ||
255 | if (!rv) | ||
256 | errExit("realloc"); | ||
257 | } | ||
258 | rv[cnt] = NULL; // end of the array | ||
259 | |||
260 | fclose(fp); | ||
261 | return rv; | ||
262 | } | ||