]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/namespace.c
namespace: make PrivateTmp= apply to both /tmp and /var/tmp
[thirdparty/systemd.git] / src / core / namespace.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
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>
32 #include <linux/fs.h>
33
34 #include "strv.h"
35 #include "util.h"
36 #include "path-util.h"
37 #include "namespace.h"
38 #include "missing.h"
39
40 typedef enum PathMode {
41 /* This is ordered by priority! */
42 INACCESSIBLE,
43 READONLY,
44 PRIVATE,
45 READWRITE
46 } PathMode;
47
48 typedef struct Path {
49 const char *path;
50 PathMode mode;
51 } Path;
52
53 static int append_paths(Path **p, char **strv, PathMode mode) {
54 char **i;
55
56 STRV_FOREACH(i, strv) {
57
58 if (!path_is_absolute(*i))
59 return -EINVAL;
60
61 (*p)->path = *i;
62 (*p)->mode = mode;
63 (*p)++;
64 }
65
66 return 0;
67 }
68
69 static int path_compare(const void *a, const void *b) {
70 const Path *p = a, *q = b;
71
72 if (path_equal(p->path, q->path)) {
73
74 /* If the paths are equal, check the mode */
75 if (p->mode < q->mode)
76 return -1;
77
78 if (p->mode > q->mode)
79 return 1;
80
81 return 0;
82 }
83
84 /* If the paths are not equal, then order prefixes first */
85 if (path_startswith(p->path, q->path))
86 return 1;
87
88 if (path_startswith(q->path, p->path))
89 return -1;
90
91 return 0;
92 }
93
94 static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible, bool *need_private) {
95 Path *f, *t, *previous;
96
97 assert(p);
98 assert(n);
99 assert(need_inaccessible);
100 assert(need_private);
101
102 for (f = p, t = p, previous = NULL; f < p+*n; f++) {
103
104 if (previous && path_equal(f->path, previous->path))
105 continue;
106
107 t->path = f->path;
108 t->mode = f->mode;
109
110 if (t->mode == PRIVATE)
111 *need_private = true;
112
113 if (t->mode == INACCESSIBLE)
114 *need_inaccessible = true;
115
116 previous = t;
117
118 t++;
119 }
120
121 *n = t - p;
122 }
123
124 static int apply_mount(Path *p, const char *root_dir, const char *inaccessible_dir, const char *private_dir, unsigned long flags) {
125 const char *what;
126 char *where;
127 int r;
128
129 assert(p);
130 assert(root_dir);
131 assert(inaccessible_dir);
132 assert(private_dir);
133
134 where = strappend(root_dir, p->path);
135 if (!where)
136 return -ENOMEM;
137
138 switch (p->mode) {
139
140 case INACCESSIBLE:
141 what = inaccessible_dir;
142 flags |= MS_RDONLY;
143 break;
144
145 case READONLY:
146 flags |= MS_RDONLY;
147 /* Fall through */
148
149 case READWRITE:
150 what = p->path;
151 break;
152
153 case PRIVATE:
154 what = private_dir;
155 break;
156
157 default:
158 assert_not_reached("Unknown mode");
159 }
160
161 r = mount(what, where, NULL, MS_BIND|MS_REC, NULL);
162 if (r >= 0) {
163 log_debug("Successfully mounted %s to %s", what, where);
164
165 /* The bind mount will always inherit the original
166 * flags. If we want to set any flag we need
167 * to do so in a second independent step. */
168 if (flags)
169 r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_REC|flags, NULL);
170
171 /* Avoid exponential growth of trees */
172 if (r >= 0 && path_equal(p->path, "/"))
173 r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|flags, NULL);
174
175 if (r < 0) {
176 r = -errno;
177 umount2(where, MNT_DETACH);
178 }
179 }
180
181 free(where);
182 return r;
183 }
184
185 int setup_namespace(
186 char **writable,
187 char **readable,
188 char **inaccessible,
189 bool private_tmp,
190 unsigned long flags) {
191
192 char
193 tmp_dir[] = "/tmp/systemd-namespace-XXXXXX",
194 root_dir[] = "/tmp/systemd-namespace-XXXXXX/root",
195 old_root_dir[] = "/tmp/systemd-namespace-XXXXXX/root/tmp/old-root-XXXXXX",
196 inaccessible_dir[] = "/tmp/systemd-namespace-XXXXXX/inaccessible",
197 private_dir[] = "/tmp/systemd-namespace-XXXXXX/private";
198
199 Path *paths, *p;
200 unsigned n;
201 bool need_private = false, need_inaccessible = false;
202 bool remove_tmp = false, remove_root = false, remove_old_root = false, remove_inaccessible = false, remove_private = false;
203 int r;
204 const char *t;
205
206 n =
207 strv_length(writable) +
208 strv_length(readable) +
209 strv_length(inaccessible) +
210 (private_tmp ? 3 : 1);
211
212 paths = new(Path, n);
213 if (!paths)
214 return -ENOMEM;
215
216 p = paths;
217 if ((r = append_paths(&p, writable, READWRITE)) < 0 ||
218 (r = append_paths(&p, readable, READONLY)) < 0 ||
219 (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0)
220 goto fail;
221
222 if (private_tmp) {
223 p->path = "/tmp";
224 p->mode = PRIVATE;
225 p++;
226
227 p->path = "/var/tmp";
228 p->mode = PRIVATE;
229 p++;
230 }
231
232 p->path = "/";
233 p->mode = READWRITE;
234 p++;
235
236 assert(paths + n == p);
237
238 qsort(paths, n, sizeof(Path), path_compare);
239 drop_duplicates(paths, &n, &need_inaccessible, &need_private);
240
241 if (!mkdtemp(tmp_dir)) {
242 r = -errno;
243 goto fail;
244 }
245 remove_tmp = true;
246
247 memcpy(root_dir, tmp_dir, sizeof(tmp_dir)-1);
248 if (mkdir(root_dir, 0777) < 0) {
249 r = -errno;
250 goto fail;
251 }
252 remove_root = true;
253
254 if (need_inaccessible) {
255 memcpy(inaccessible_dir, tmp_dir, sizeof(tmp_dir)-1);
256 if (mkdir(inaccessible_dir, 0) < 0) {
257 r = -errno;
258 goto fail;
259 }
260 remove_inaccessible = true;
261 }
262
263 if (need_private) {
264 mode_t u;
265
266 memcpy(private_dir, tmp_dir, sizeof(tmp_dir)-1);
267
268 u = umask(0000);
269 if (mkdir(private_dir, 0777 + S_ISVTX) < 0) {
270 umask(u);
271
272 r = -errno;
273 goto fail;
274 }
275
276 umask(u);
277 remove_private = true;
278 }
279
280 if (unshare(CLONE_NEWNS) < 0) {
281 r = -errno;
282 goto fail;
283 }
284
285 /* Remount / as SLAVE so that nothing mounted in the namespace
286 shows up in the parent */
287 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
288 r = -errno;
289 goto fail;
290 }
291
292 for (p = paths; p < paths + n; p++) {
293 r = apply_mount(p, root_dir, inaccessible_dir, private_dir, flags);
294 if (r < 0)
295 goto undo_mounts;
296 }
297
298 memcpy(old_root_dir, tmp_dir, sizeof(tmp_dir)-1);
299 if (!mkdtemp(old_root_dir)) {
300 r = -errno;
301 goto undo_mounts;
302 }
303 remove_old_root = true;
304
305 if (chdir(root_dir) < 0) {
306 r = -errno;
307 goto undo_mounts;
308 }
309
310 if (pivot_root(root_dir, old_root_dir) < 0) {
311 r = -errno;
312 goto undo_mounts;
313 }
314
315 t = old_root_dir + sizeof(root_dir) - 1;
316 if (umount2(t, MNT_DETACH) < 0)
317 /* At this point it's too late to turn anything back,
318 * since we are already in the new root. */
319 return -errno;
320
321 if (rmdir(t) < 0)
322 return -errno;
323
324 return 0;
325
326 undo_mounts:
327
328 for (p--; p >= paths; p--) {
329 char full_path[PATH_MAX];
330
331 snprintf(full_path, sizeof(full_path), "%s%s", root_dir, p->path);
332 char_array_0(full_path);
333
334 umount2(full_path, MNT_DETACH);
335 }
336
337 fail:
338 if (remove_old_root)
339 rmdir(old_root_dir);
340
341 if (remove_inaccessible)
342 rmdir(inaccessible_dir);
343
344 if (remove_private)
345 rmdir(private_dir);
346
347 if (remove_root)
348 rmdir(root_dir);
349
350 if (remove_tmp)
351 rmdir(tmp_dir);
352
353 free(paths);
354
355 return r;
356 }