chiark / gitweb /
Make PrivateTmp dirs also inaccessible from the outside
[elogind.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 #include "execute.h"
40
41 typedef enum MountMode {
42         /* This is ordered by priority! */
43         INACCESSIBLE,
44         READONLY,
45         PRIVATE_TMP,
46         PRIVATE_VAR_TMP,
47         READWRITE
48 } MountMode;
49
50 typedef struct BindMount {
51         const char *path;
52         MountMode mode;
53         bool done;
54 } BindMount;
55
56 static int append_mounts(BindMount **p, char **strv, MountMode mode) {
57         char **i;
58
59         STRV_FOREACH(i, strv) {
60
61                 if (!path_is_absolute(*i))
62                         return -EINVAL;
63
64                 (*p)->path = *i;
65                 (*p)->mode = mode;
66                 (*p)++;
67         }
68
69         return 0;
70 }
71
72 static int mount_path_compare(const void *a, const void *b) {
73         const BindMount *p = a, *q = b;
74
75         if (path_equal(p->path, q->path)) {
76
77                 /* If the paths are equal, check the mode */
78                 if (p->mode < q->mode)
79                         return -1;
80
81                 if (p->mode > q->mode)
82                         return 1;
83
84                 return 0;
85         }
86
87         /* If the paths are not equal, then order prefixes first */
88         if (path_startswith(p->path, q->path))
89                 return 1;
90
91         if (path_startswith(q->path, p->path))
92                 return -1;
93
94         return 0;
95 }
96
97 static void drop_duplicates(BindMount *m, unsigned *n) {
98         BindMount *f, *t, *previous;
99
100         assert(m);
101         assert(n);
102
103         for (f = m, t = m, previous = NULL; f < m+*n; f++) {
104
105                 /* The first one wins */
106                 if (previous && path_equal(f->path, previous->path))
107                         continue;
108
109                 t->path = f->path;
110                 t->mode = f->mode;
111
112                 previous = t;
113
114                 t++;
115         }
116
117         *n = t - m;
118 }
119
120 static int apply_mount(
121                 BindMount *m,
122                 const char *tmp_dir,
123                 const char *var_tmp_dir) {
124
125         const char *what;
126         int r;
127
128         assert(m);
129
130         switch (m->mode) {
131
132         case INACCESSIBLE:
133                 what = "/run/systemd/inaccessible";
134                 break;
135
136         case READONLY:
137         case READWRITE:
138                 what = m->path;
139                 break;
140
141         case PRIVATE_TMP:
142                 what = tmp_dir;
143                 break;
144
145         case PRIVATE_VAR_TMP:
146                 what = var_tmp_dir;
147                 break;
148
149         default:
150                 assert_not_reached("Unknown mode");
151         }
152
153         assert(what);
154
155         r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
156         if (r >= 0)
157                 log_debug("Successfully mounted %s to %s", what, m->path);
158
159         return r;
160 }
161
162 static int make_read_only(BindMount *m) {
163         int r;
164
165         assert(m);
166
167         if (m->mode != INACCESSIBLE && m->mode != READONLY)
168                 return 0;
169
170         r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
171         if (r < 0)
172                 return -errno;
173
174         return 0;
175 }
176
177 int setup_tmpdirs(char **tmp_dir,
178                   char **var_tmp_dir) {
179         int r = 0;
180         char tmp_dir_template[] = "/tmp/systemd-private-XXXXXX",
181              var_tmp_dir_template[] = "/var/tmp/systemd-private-XXXXXX";
182
183         assert(tmp_dir);
184         assert(var_tmp_dir);
185
186         r = create_tmp_dir(tmp_dir_template, tmp_dir);
187         if (r < 0)
188                 return r;
189
190         r = create_tmp_dir(var_tmp_dir_template, var_tmp_dir);
191         if (r == 0)
192                 return 0;
193
194         /* failure */
195         rmdir(*tmp_dir);
196         rmdir(tmp_dir_template);
197         free(*tmp_dir);
198         *tmp_dir = NULL;
199
200         return r;
201 }
202
203 int setup_namespace(char** read_write_dirs,
204                     char** read_only_dirs,
205                     char** inaccessible_dirs,
206                     char* tmp_dir,
207                     char* var_tmp_dir,
208                     bool private_tmp,
209                     unsigned mount_flags) {
210
211         unsigned n = strv_length(read_write_dirs) +
212                      strv_length(read_only_dirs) +
213                      strv_length(inaccessible_dirs) +
214                      (private_tmp ? 2 : 0);
215         BindMount *m, *mounts;
216         int r = 0;
217
218         if (!mount_flags)
219                 mount_flags = MS_SHARED;
220
221         if (unshare(CLONE_NEWNS) < 0) {
222                 r = -errno;
223                 goto fail;
224         }
225
226         m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
227         if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 ||
228                 (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 ||
229                 (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0)
230                 goto fail;
231
232         if (private_tmp) {
233                 m->path = "/tmp";
234                 m->mode = PRIVATE_TMP;
235                 m++;
236
237                 m->path = "/var/tmp";
238                 m->mode = PRIVATE_VAR_TMP;
239                 m++;
240         }
241
242         assert(mounts + n == m);
243
244         qsort(mounts, n, sizeof(BindMount), mount_path_compare);
245         drop_duplicates(mounts, &n);
246
247         /* Remount / as SLAVE so that nothing now mounted in the namespace
248            shows up in the parent */
249         if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
250                 r = -errno;
251                 goto fail;
252         }
253
254         for (m = mounts; m < mounts + n; ++m) {
255                 r = apply_mount(m, tmp_dir, var_tmp_dir);
256                 if (r < 0)
257                         goto undo_mounts;
258         }
259
260         for (m = mounts; m < mounts + n; ++m) {
261                 r = make_read_only(m);
262                 if (r < 0)
263                         goto undo_mounts;
264         }
265
266         /* Remount / as the desired mode */
267         if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
268                 r = -errno;
269                 goto undo_mounts;
270         }
271
272         return 0;
273
274 undo_mounts:
275         for (m = mounts; m < mounts + n; ++m) {
276                 if (m->done)
277                         umount2(m->path, MNT_DETACH);
278         }
279
280 fail:
281         return r;
282 }