]>
Commit | Line | Data |
---|---|---|
d6c9574f | 1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
15ae422b LP |
2 | |
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2010 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
5430f7f2 LP |
9 | under the terms of the GNU Lesser General Public License as published by |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
15ae422b LP |
11 | (at your option) any later version. |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
5430f7f2 | 16 | Lesser General Public License for more details. |
15ae422b | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
15ae422b LP |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. |
20 | ***/ | |
21 | ||
22 | #include <errno.h> | |
23 | #include <sys/mount.h> | |
24 | #include <string.h> | |
25 | #include <stdio.h> | |
26 | #include <unistd.h> | |
27 | #include <sys/stat.h> | |
28 | #include <sys/types.h> | |
29 | #include <sched.h> | |
30 | #include <sys/syscall.h> | |
31 | #include <limits.h> | |
25e870b5 | 32 | #include <linux/fs.h> |
15ae422b LP |
33 | |
34 | #include "strv.h" | |
35 | #include "util.h" | |
9eb977db | 36 | #include "path-util.h" |
15ae422b LP |
37 | #include "namespace.h" |
38 | #include "missing.h" | |
39 | ||
40 | typedef enum PathMode { | |
41 | /* This is ordered by priority! */ | |
42 | INACCESSIBLE, | |
43 | READONLY, | |
ac0930c8 LP |
44 | PRIVATE_TMP, |
45 | PRIVATE_VAR_TMP, | |
15ae422b LP |
46 | READWRITE |
47 | } PathMode; | |
48 | ||
49 | typedef struct Path { | |
50 | const char *path; | |
51 | PathMode mode; | |
ac0930c8 | 52 | bool done; |
15ae422b LP |
53 | } Path; |
54 | ||
55 | static int append_paths(Path **p, char **strv, PathMode mode) { | |
56 | char **i; | |
57 | ||
58 | STRV_FOREACH(i, strv) { | |
59 | ||
60 | if (!path_is_absolute(*i)) | |
61 | return -EINVAL; | |
62 | ||
63 | (*p)->path = *i; | |
64 | (*p)->mode = mode; | |
65 | (*p)++; | |
66 | } | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
71 | static int path_compare(const void *a, const void *b) { | |
72 | const Path *p = a, *q = b; | |
73 | ||
74 | if (path_equal(p->path, q->path)) { | |
75 | ||
76 | /* If the paths are equal, check the mode */ | |
77 | if (p->mode < q->mode) | |
78 | return -1; | |
79 | ||
80 | if (p->mode > q->mode) | |
81 | return 1; | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | /* If the paths are not equal, then order prefixes first */ | |
87 | if (path_startswith(p->path, q->path)) | |
88 | return 1; | |
89 | ||
90 | if (path_startswith(q->path, p->path)) | |
91 | return -1; | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
ac0930c8 | 96 | static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) { |
15ae422b LP |
97 | Path *f, *t, *previous; |
98 | ||
99 | assert(p); | |
100 | assert(n); | |
101 | assert(need_inaccessible); | |
15ae422b LP |
102 | |
103 | for (f = p, t = p, previous = NULL; f < p+*n; f++) { | |
104 | ||
ac0930c8 | 105 | /* The first one wins */ |
15ae422b LP |
106 | if (previous && path_equal(f->path, previous->path)) |
107 | continue; | |
108 | ||
109 | t->path = f->path; | |
110 | t->mode = f->mode; | |
111 | ||
15ae422b LP |
112 | if (t->mode == INACCESSIBLE) |
113 | *need_inaccessible = true; | |
114 | ||
115 | previous = t; | |
116 | ||
117 | t++; | |
118 | } | |
119 | ||
120 | *n = t - p; | |
121 | } | |
122 | ||
ac0930c8 LP |
123 | static int apply_mount( |
124 | Path *p, | |
125 | const char *tmp_dir, | |
126 | const char *var_tmp_dir, | |
127 | const char *inaccessible_dir) { | |
128 | ||
15ae422b | 129 | const char *what; |
15ae422b | 130 | int r; |
15ae422b LP |
131 | |
132 | assert(p); | |
15ae422b LP |
133 | |
134 | switch (p->mode) { | |
135 | ||
136 | case INACCESSIBLE: | |
137 | what = inaccessible_dir; | |
15ae422b LP |
138 | break; |
139 | ||
140 | case READONLY: | |
15ae422b LP |
141 | case READWRITE: |
142 | what = p->path; | |
143 | break; | |
144 | ||
ac0930c8 LP |
145 | case PRIVATE_TMP: |
146 | what = tmp_dir; | |
147 | break; | |
148 | ||
149 | case PRIVATE_VAR_TMP: | |
150 | what = var_tmp_dir; | |
15ae422b | 151 | break; |
e364ad06 LP |
152 | |
153 | default: | |
154 | assert_not_reached("Unknown mode"); | |
15ae422b LP |
155 | } |
156 | ||
ac0930c8 | 157 | assert(what); |
15ae422b | 158 | |
1e41be20 | 159 | r = mount(what, p->path, NULL, MS_BIND|MS_REC, NULL); |
ac0930c8 LP |
160 | if (r >= 0) |
161 | log_debug("Successfully mounted %s to %s", what, p->path); | |
15ae422b | 162 | |
ac0930c8 LP |
163 | return r; |
164 | } | |
15ae422b | 165 | |
ac0930c8 LP |
166 | static int make_read_only(Path *p) { |
167 | int r; | |
15ae422b | 168 | |
ac0930c8 LP |
169 | assert(p); |
170 | ||
171 | if (p->mode != INACCESSIBLE && p->mode != READONLY) | |
172 | return 0; | |
173 | ||
1e41be20 | 174 | r = mount(NULL, p->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL); |
ac0930c8 LP |
175 | if (r < 0) |
176 | return -errno; | |
177 | ||
178 | return 0; | |
15ae422b LP |
179 | } |
180 | ||
181 | int setup_namespace( | |
182 | char **writable, | |
183 | char **readable, | |
184 | char **inaccessible, | |
185 | bool private_tmp, | |
186 | unsigned long flags) { | |
187 | ||
188 | char | |
ac0930c8 LP |
189 | tmp_dir[] = "/tmp/systemd-private-XXXXXX", |
190 | var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX", | |
191 | inaccessible_dir[] = "/tmp/systemd-inaccessible-XXXXXX"; | |
15ae422b LP |
192 | |
193 | Path *paths, *p; | |
194 | unsigned n; | |
ac0930c8 LP |
195 | bool need_inaccessible = false; |
196 | bool remove_tmp = false, remove_var_tmp = false, remove_inaccessible = false; | |
15ae422b | 197 | int r; |
ac0930c8 LP |
198 | |
199 | if (!flags) | |
200 | flags = MS_SHARED; | |
15ae422b LP |
201 | |
202 | n = | |
203 | strv_length(writable) + | |
204 | strv_length(readable) + | |
205 | strv_length(inaccessible) + | |
ac0930c8 | 206 | (private_tmp ? 2 : 0); |
15ae422b | 207 | |
ac0930c8 | 208 | p = paths = alloca(sizeof(Path) * n); |
15ae422b LP |
209 | if ((r = append_paths(&p, writable, READWRITE)) < 0 || |
210 | (r = append_paths(&p, readable, READONLY)) < 0 || | |
211 | (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0) | |
212 | goto fail; | |
213 | ||
214 | if (private_tmp) { | |
215 | p->path = "/tmp"; | |
ac0930c8 | 216 | p->mode = PRIVATE_TMP; |
15ae422b | 217 | p++; |
c1d70f7c LP |
218 | |
219 | p->path = "/var/tmp"; | |
ac0930c8 | 220 | p->mode = PRIVATE_VAR_TMP; |
c1d70f7c | 221 | p++; |
15ae422b LP |
222 | } |
223 | ||
15ae422b LP |
224 | assert(paths + n == p); |
225 | ||
226 | qsort(paths, n, sizeof(Path), path_compare); | |
ac0930c8 | 227 | drop_duplicates(paths, &n, &need_inaccessible); |
15ae422b | 228 | |
ac0930c8 LP |
229 | if (need_inaccessible) { |
230 | mode_t u; | |
231 | char *d; | |
15ae422b | 232 | |
ac0930c8 LP |
233 | u = umask(0777); |
234 | d = mkdtemp(inaccessible_dir); | |
235 | umask(u); | |
15ae422b | 236 | |
ac0930c8 | 237 | if (!d) { |
15ae422b LP |
238 | r = -errno; |
239 | goto fail; | |
240 | } | |
ac0930c8 | 241 | |
15ae422b LP |
242 | remove_inaccessible = true; |
243 | } | |
244 | ||
ac0930c8 | 245 | if (private_tmp) { |
21d279cf | 246 | mode_t u; |
ac0930c8 | 247 | char *d; |
21d279cf LP |
248 | |
249 | u = umask(0000); | |
ac0930c8 LP |
250 | d = mkdtemp(tmp_dir); |
251 | umask(u); | |
21d279cf | 252 | |
ac0930c8 | 253 | if (!d) { |
15ae422b LP |
254 | r = -errno; |
255 | goto fail; | |
256 | } | |
21d279cf | 257 | |
ac0930c8 LP |
258 | remove_tmp = true; |
259 | ||
260 | u = umask(0000); | |
261 | d = mkdtemp(var_tmp_dir); | |
21d279cf | 262 | umask(u); |
ac0930c8 LP |
263 | |
264 | if (!d) { | |
265 | r = -errno; | |
266 | goto fail; | |
267 | } | |
268 | ||
269 | remove_var_tmp = true; | |
270 | ||
271 | if (chmod(tmp_dir, 0777 + S_ISVTX) < 0) { | |
272 | r = -errno; | |
273 | goto fail; | |
274 | } | |
275 | ||
276 | if (chmod(var_tmp_dir, 0777 + S_ISVTX) < 0) { | |
277 | r = -errno; | |
278 | goto fail; | |
279 | } | |
15ae422b LP |
280 | } |
281 | ||
282 | if (unshare(CLONE_NEWNS) < 0) { | |
283 | r = -errno; | |
284 | goto fail; | |
285 | } | |
286 | ||
ac0930c8 | 287 | /* Remount / as SLAVE so that nothing now mounted in the namespace |
dc4b0200 DW |
288 | shows up in the parent */ |
289 | if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) { | |
290 | r = -errno; | |
291 | goto fail; | |
292 | } | |
15ae422b | 293 | |
c1d70f7c | 294 | for (p = paths; p < paths + n; p++) { |
ac0930c8 | 295 | r = apply_mount(p, tmp_dir, var_tmp_dir, inaccessible_dir); |
c1d70f7c | 296 | if (r < 0) |
15ae422b | 297 | goto undo_mounts; |
c1d70f7c | 298 | } |
15ae422b | 299 | |
ac0930c8 LP |
300 | for (p = paths; p < paths + n; p++) { |
301 | r = make_read_only(p); | |
302 | if (r < 0) | |
303 | goto undo_mounts; | |
15ae422b LP |
304 | } |
305 | ||
ac0930c8 LP |
306 | /* Remount / as the desired mode */ |
307 | if (mount(NULL, "/", NULL, flags|MS_REC, NULL) < 0) { | |
15ae422b LP |
308 | r = -errno; |
309 | goto undo_mounts; | |
310 | } | |
311 | ||
15ae422b LP |
312 | return 0; |
313 | ||
314 | undo_mounts: | |
ac0930c8 LP |
315 | for (p = paths; p < paths + n; p++) |
316 | if (p->done) | |
317 | umount2(p->path, MNT_DETACH); | |
15ae422b LP |
318 | |
319 | fail: | |
15ae422b LP |
320 | if (remove_inaccessible) |
321 | rmdir(inaccessible_dir); | |
322 | ||
15ae422b LP |
323 | if (remove_tmp) |
324 | rmdir(tmp_dir); | |
325 | ||
ac0930c8 LP |
326 | if (remove_var_tmp) |
327 | rmdir(var_tmp_dir); | |
15ae422b LP |
328 | |
329 | return r; | |
330 | } |