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