chiark / gitweb /
namespace: when setting up an inaccessible mount point, unmounting everything below
[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 #include <sys/file.h>
34
35 #include "strv.h"
36 #include "util.h"
37 #include "path-util.h"
38 #include "namespace.h"
39 #include "missing.h"
40 #include "execute.h"
41 #include "loopback-setup.h"
42 #include "mkdir.h"
43 #include "dev-setup.h"
44 #include "def.h"
45
46 typedef enum MountMode {
47         /* This is ordered by priority! */
48         INACCESSIBLE,
49         READONLY,
50         PRIVATE_TMP,
51         PRIVATE_VAR_TMP,
52         PRIVATE_DEV,
53         READWRITE
54 } MountMode;
55
56 typedef struct BindMount {
57         const char *path;
58         MountMode mode;
59         bool done;
60         bool ignore;
61 } BindMount;
62
63 static int append_mounts(BindMount **p, char **strv, MountMode mode) {
64         char **i;
65
66         assert(p);
67
68         STRV_FOREACH(i, strv) {
69
70                 (*p)->ignore = false;
71
72                 if ((mode == INACCESSIBLE || mode == READONLY || mode == READWRITE) && (*i)[0] == '-') {
73                         (*p)->ignore = true;
74                         (*i)++;
75                 }
76
77                 if (!path_is_absolute(*i))
78                         return -EINVAL;
79
80                 (*p)->path = *i;
81                 (*p)->mode = mode;
82                 (*p)++;
83         }
84
85         return 0;
86 }
87
88 static int mount_path_compare(const void *a, const void *b) {
89         const BindMount *p = a, *q = b;
90
91         if (path_equal(p->path, q->path)) {
92
93                 /* If the paths are equal, check the mode */
94                 if (p->mode < q->mode)
95                         return -1;
96
97                 if (p->mode > q->mode)
98                         return 1;
99
100                 return 0;
101         }
102
103         /* If the paths are not equal, then order prefixes first */
104         if (path_startswith(p->path, q->path))
105                 return 1;
106
107         if (path_startswith(q->path, p->path))
108                 return -1;
109
110         return 0;
111 }
112
113 static void drop_duplicates(BindMount *m, unsigned *n) {
114         BindMount *f, *t, *previous;
115
116         assert(m);
117         assert(n);
118
119         for (f = m, t = m, previous = NULL; f < m+*n; f++) {
120
121                 /* The first one wins */
122                 if (previous && path_equal(f->path, previous->path))
123                         continue;
124
125                 t->path = f->path;
126                 t->mode = f->mode;
127
128                 previous = t;
129
130                 t++;
131         }
132
133         *n = t - m;
134 }
135
136 static int mount_dev(BindMount *m) {
137         static const char devnodes[] =
138                 "/dev/null\0"
139                 "/dev/zero\0"
140                 "/dev/full\0"
141                 "/dev/random\0"
142                 "/dev/urandom\0"
143                 "/dev/tty\0";
144
145         char temporary_mount[] = "/tmp/namespace-dev-XXXXXX";
146         const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devkdbus = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
147         _cleanup_umask_ mode_t u;
148         int r;
149
150         assert(m);
151
152         u = umask(0000);
153
154         if (!mkdtemp(temporary_mount))
155                 return -errno;
156
157         dev = strappenda(temporary_mount, "/dev");
158         mkdir(dev, 0755);
159         if (mount("tmpfs", dev, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=755") < 0) {
160                 r = -errno;
161                 goto fail;
162         }
163
164         devpts = strappenda(temporary_mount, "/dev/pts");
165         mkdir(devpts, 0755);
166         if (mount("/dev/pts", devpts, NULL, MS_BIND, NULL) < 0) {
167                 r = -errno;
168                 goto fail;
169         }
170
171         devptmx = strappenda(temporary_mount, "/dev/ptmx");
172         symlink("pts/ptmx", devptmx);
173
174         devshm = strappenda(temporary_mount, "/dev/shm");
175         mkdir(devshm, 01777);
176         r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL);
177         if (r < 0) {
178                 r = -errno;
179                 goto fail;
180         }
181
182         devmqueue = strappenda(temporary_mount, "/dev/mqueue");
183         mkdir(devmqueue, 0755);
184         mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL);
185
186         devkdbus = strappenda(temporary_mount, "/dev/kdbus");
187         mkdir(devkdbus, 0755);
188         mount("/dev/kdbus", devkdbus, NULL, MS_BIND, NULL);
189
190         devhugepages = strappenda(temporary_mount, "/dev/hugepages");
191         mkdir(devhugepages, 0755);
192         mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL);
193
194         devlog = strappenda(temporary_mount, "/dev/log");
195         symlink("/run/systemd/journal/dev-log", devlog);
196
197         NULSTR_FOREACH(d, devnodes) {
198                 _cleanup_free_ char *dn = NULL;
199                 struct stat st;
200
201                 r = stat(d, &st);
202                 if (r < 0) {
203
204                         if (errno == ENOENT)
205                                 continue;
206
207                         r = -errno;
208                         goto fail;
209                 }
210
211                 if (!S_ISBLK(st.st_mode) &&
212                     !S_ISCHR(st.st_mode)) {
213                         r = -EINVAL;
214                         goto fail;
215                 }
216
217                 if (st.st_rdev == 0)
218                         continue;
219
220                 dn = strappend(temporary_mount, d);
221                 if (!dn) {
222                         r = -ENOMEM;
223                         goto fail;
224                 }
225
226                 r = mknod(dn, st.st_mode, st.st_rdev);
227                 if (r < 0) {
228                         r = -errno;
229                         goto fail;
230                 }
231         }
232
233         dev_setup(temporary_mount);
234
235         if (mount(dev, "/dev/", NULL, MS_MOVE, NULL) < 0) {
236                 r = -errno;
237                 goto fail;
238         }
239
240         rmdir(dev);
241         rmdir(temporary_mount);
242
243         return 0;
244
245 fail:
246         if (devpts)
247                 umount(devpts);
248
249         if (devshm)
250                 umount(devshm);
251
252         if (devkdbus)
253                 umount(devkdbus);
254
255         if (devhugepages)
256                 umount(devhugepages);
257
258         if (devmqueue)
259                 umount(devmqueue);
260
261         if (dev) {
262                 umount(dev);
263                 rmdir(dev);
264         }
265
266         rmdir(temporary_mount);
267
268         return r;
269 }
270
271 static int apply_mount(
272                 BindMount *m,
273                 const char *tmp_dir,
274                 const char *var_tmp_dir) {
275
276         const char *what;
277         int r;
278
279         assert(m);
280
281         switch (m->mode) {
282
283         case PRIVATE_DEV:
284                 return mount_dev(m);
285
286         case INACCESSIBLE:
287
288                 /* First, get rid of everything that is below if there
289                  * is anything... Then, overmount it with an
290                  * inaccessible directory. */
291                 umount_recursive(m->path, 0);
292
293                 what = "/run/systemd/inaccessible";
294                 break;
295
296         case READONLY:
297         case READWRITE:
298                 what = m->path;
299                 break;
300
301         case PRIVATE_TMP:
302                 what = tmp_dir;
303                 break;
304
305         case PRIVATE_VAR_TMP:
306                 what = var_tmp_dir;
307                 break;
308
309         default:
310                 assert_not_reached("Unknown mode");
311         }
312
313         assert(what);
314
315         r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
316         if (r >= 0)
317                 log_debug("Successfully mounted %s to %s", what, m->path);
318         else if (m->ignore && errno == ENOENT)
319                 r = 0;
320
321         return r;
322 }
323
324 static int make_read_only(BindMount *m) {
325         int r;
326
327         assert(m);
328
329         if (m->mode != INACCESSIBLE && m->mode != READONLY)
330                 return 0;
331
332         r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
333         if (r < 0 && !(m->ignore && errno == ENOENT))
334                 return -errno;
335
336         return 0;
337 }
338
339 int setup_namespace(
340                 char** read_write_dirs,
341                 char** read_only_dirs,
342                 char** inaccessible_dirs,
343                 char* tmp_dir,
344                 char* var_tmp_dir,
345                 bool private_dev,
346                 ProtectHome protect_home,
347                 ProtectSystem protect_system,
348                 unsigned mount_flags) {
349
350         BindMount *m, *mounts = NULL;
351         unsigned n;
352         int r = 0;
353
354         if (mount_flags == 0)
355                 mount_flags = MS_SHARED;
356
357         if (unshare(CLONE_NEWNS) < 0)
358                 return -errno;
359
360         n = !!tmp_dir + !!var_tmp_dir +
361                 strv_length(read_write_dirs) +
362                 strv_length(read_only_dirs) +
363                 strv_length(inaccessible_dirs) +
364                 private_dev +
365                 (protect_home != PROTECT_HOME_NO ? 2 : 0) +
366                 (protect_system != PROTECT_SYSTEM_NO ? 1 : 0) +
367                 (protect_system == PROTECT_SYSTEM_FULL ? 1 : 0);
368
369         if (n > 0) {
370                 m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
371                 r = append_mounts(&m, read_write_dirs, READWRITE);
372                 if (r < 0)
373                         return r;
374
375                 r = append_mounts(&m, read_only_dirs, READONLY);
376                 if (r < 0)
377                         return r;
378
379                 r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE);
380                 if (r < 0)
381                         return r;
382
383                 if (tmp_dir) {
384                         m->path = "/tmp";
385                         m->mode = PRIVATE_TMP;
386                         m++;
387                 }
388
389                 if (var_tmp_dir) {
390                         m->path = "/var/tmp";
391                         m->mode = PRIVATE_VAR_TMP;
392                         m++;
393                 }
394
395                 if (private_dev) {
396                         m->path = "/dev";
397                         m->mode = PRIVATE_DEV;
398                         m++;
399                 }
400
401                 if (protect_home != PROTECT_HOME_NO) {
402                         r = append_mounts(&m, STRV_MAKE("-/home", "-/run/user"), protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE);
403                         if (r < 0)
404                                 return r;
405                 }
406
407                 if (protect_system != PROTECT_SYSTEM_NO) {
408                         r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL ? STRV_MAKE("/usr", "/etc") : STRV_MAKE("/usr"), READONLY);
409                         if (r < 0)
410                                 return r;
411                 }
412
413                 assert(mounts + n == m);
414
415                 qsort(mounts, n, sizeof(BindMount), mount_path_compare);
416                 drop_duplicates(mounts, &n);
417         }
418
419         if (n > 0) {
420                 /* Remount / as SLAVE so that nothing now mounted in the namespace
421                    shows up in the parent */
422                 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
423                         return -errno;
424
425                 for (m = mounts; m < mounts + n; ++m) {
426                         r = apply_mount(m, tmp_dir, var_tmp_dir);
427                         if (r < 0)
428                                 goto fail;
429                 }
430
431                 for (m = mounts; m < mounts + n; ++m) {
432                         r = make_read_only(m);
433                         if (r < 0)
434                                 goto fail;
435                 }
436         }
437
438         /* Remount / as the desired mode. Not that this will not
439          * reestablish propagation from our side to the host, since
440          * what's disconnected is disconnected. */
441         if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
442                 r = -errno;
443                 goto fail;
444         }
445
446         return 0;
447
448 fail:
449         if (n > 0) {
450                 for (m = mounts; m < mounts + n; ++m)
451                         if (m->done)
452                                 umount2(m->path, MNT_DETACH);
453         }
454
455         return r;
456 }
457
458 static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
459         _cleanup_free_ char *x = NULL;
460         char bid[SD_ID128_STRING_MAX];
461         sd_id128_t boot_id;
462         int r;
463
464         assert(id);
465         assert(prefix);
466         assert(path);
467
468         /* We include the boot id in the directory so that after a
469          * reboot we can easily identify obsolete directories. */
470
471         r = sd_id128_get_boot(&boot_id);
472         if (r < 0)
473                 return r;
474
475         x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX", NULL);
476         if (!x)
477                 return -ENOMEM;
478
479         RUN_WITH_UMASK(0077)
480                 if (!mkdtemp(x))
481                         return -errno;
482
483         RUN_WITH_UMASK(0000) {
484                 char *y;
485
486                 y = strappenda(x, "/tmp");
487
488                 if (mkdir(y, 0777 | S_ISVTX) < 0)
489                         return -errno;
490         }
491
492         *path = x;
493         x = NULL;
494
495         return 0;
496 }
497
498 int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
499         char *a, *b;
500         int r;
501
502         assert(id);
503         assert(tmp_dir);
504         assert(var_tmp_dir);
505
506         r = setup_one_tmp_dir(id, "/tmp", &a);
507         if (r < 0)
508                 return r;
509
510         r = setup_one_tmp_dir(id, "/var/tmp", &b);
511         if (r < 0) {
512                 char *t;
513
514                 t = strappenda(a, "/tmp");
515                 rmdir(t);
516                 rmdir(a);
517
518                 free(a);
519                 return r;
520         }
521
522         *tmp_dir = a;
523         *var_tmp_dir = b;
524
525         return 0;
526 }
527
528 int setup_netns(int netns_storage_socket[2]) {
529         _cleanup_close_ int netns = -1;
530         union {
531                 struct cmsghdr cmsghdr;
532                 uint8_t buf[CMSG_SPACE(sizeof(int))];
533         } control = {};
534         struct msghdr mh = {
535                 .msg_control = &control,
536                 .msg_controllen = sizeof(control),
537         };
538         struct cmsghdr *cmsg;
539         int r;
540
541         assert(netns_storage_socket);
542         assert(netns_storage_socket[0] >= 0);
543         assert(netns_storage_socket[1] >= 0);
544
545         /* We use the passed socketpair as a storage buffer for our
546          * namespace reference fd. Whatever process runs this first
547          * shall create a new namespace, all others should just join
548          * it. To serialize that we use a file lock on the socket
549          * pair.
550          *
551          * It's a bit crazy, but hey, works great! */
552
553         if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0)
554                 return -errno;
555
556         if (recvmsg(netns_storage_socket[0], &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC) < 0) {
557                 if (errno != EAGAIN) {
558                         r = -errno;
559                         goto fail;
560                 }
561
562                 /* Nothing stored yet, so let's create a new namespace */
563
564                 if (unshare(CLONE_NEWNET) < 0) {
565                         r = -errno;
566                         goto fail;
567                 }
568
569                 loopback_setup();
570
571                 netns = open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC|O_NOCTTY);
572                 if (netns < 0) {
573                         r = -errno;
574                         goto fail;
575                 }
576
577                 r = 1;
578         } else {
579                 /* Yay, found something, so let's join the namespace */
580
581                 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
582                         if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
583                                 assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
584                                 netns = *(int*) CMSG_DATA(cmsg);
585                         }
586                 }
587
588                 if (setns(netns, CLONE_NEWNET) < 0) {
589                         r = -errno;
590                         goto fail;
591                 }
592
593                 r = 0;
594         }
595
596         cmsg = CMSG_FIRSTHDR(&mh);
597         cmsg->cmsg_level = SOL_SOCKET;
598         cmsg->cmsg_type = SCM_RIGHTS;
599         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
600         memcpy(CMSG_DATA(cmsg), &netns, sizeof(int));
601         mh.msg_controllen = cmsg->cmsg_len;
602
603         if (sendmsg(netns_storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL) < 0) {
604                 r = -errno;
605                 goto fail;
606         }
607
608 fail:
609         lockf(netns_storage_socket[0], F_ULOCK, 0);
610
611         return r;
612 }
613
614 static const char *const protect_home_table[_PROTECT_HOME_MAX] = {
615         [PROTECT_HOME_NO] = "no",
616         [PROTECT_HOME_YES] = "yes",
617         [PROTECT_HOME_READ_ONLY] = "read-only",
618 };
619
620 DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome);
621
622 static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
623         [PROTECT_SYSTEM_NO] = "no",
624         [PROTECT_SYSTEM_YES] = "yes",
625         [PROTECT_SYSTEM_FULL] = "full",
626 };
627
628 DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem);