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