Coverage Report

Created: 2019-06-19 13:33

/src/systemd/src/basic/namespace-util.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1+ */
2
3
#include <fcntl.h>
4
#include <linux/magic.h>
5
6
#include "fd-util.h"
7
#include "missing.h"
8
#include "namespace-util.h"
9
#include "process-util.h"
10
#include "stat-util.h"
11
#include "user-util.h"
12
13
0
int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
14
0
        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
15
0
        int rfd = -1;
16
0
17
0
        assert(pid >= 0);
18
0
19
0
        if (mntns_fd) {
20
0
                const char *mntns;
21
0
22
0
                mntns = procfs_file_alloca(pid, "ns/mnt");
23
0
                mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
24
0
                if (mntnsfd < 0)
25
0
                        return -errno;
26
0
        }
27
0
28
0
        if (pidns_fd) {
29
0
                const char *pidns;
30
0
31
0
                pidns = procfs_file_alloca(pid, "ns/pid");
32
0
                pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
33
0
                if (pidnsfd < 0)
34
0
                        return -errno;
35
0
        }
36
0
37
0
        if (netns_fd) {
38
0
                const char *netns;
39
0
40
0
                netns = procfs_file_alloca(pid, "ns/net");
41
0
                netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
42
0
                if (netnsfd < 0)
43
0
                        return -errno;
44
0
        }
45
0
46
0
        if (userns_fd) {
47
0
                const char *userns;
48
0
49
0
                userns = procfs_file_alloca(pid, "ns/user");
50
0
                usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
51
0
                if (usernsfd < 0 && errno != ENOENT)
52
0
                        return -errno;
53
0
        }
54
0
55
0
        if (root_fd) {
56
0
                const char *root;
57
0
58
0
                root = procfs_file_alloca(pid, "root");
59
0
                rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
60
0
                if (rfd < 0)
61
0
                        return -errno;
62
0
        }
63
0
64
0
        if (pidns_fd)
65
0
                *pidns_fd = TAKE_FD(pidnsfd);
66
0
67
0
        if (mntns_fd)
68
0
                *mntns_fd = TAKE_FD(mntnsfd);
69
0
70
0
        if (netns_fd)
71
0
                *netns_fd = TAKE_FD(netnsfd);
72
0
73
0
        if (userns_fd)
74
0
                *userns_fd = TAKE_FD(usernsfd);
75
0
76
0
        if (root_fd)
77
0
                *root_fd = TAKE_FD(rfd);
78
0
79
0
        return 0;
80
0
}
81
82
0
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
83
0
        if (userns_fd >= 0) {
84
0
                /* Can't setns to your own userns, since then you could
85
0
                 * escalate from non-root to root in your own namespace, so
86
0
                 * check if namespaces equal before attempting to enter. */
87
0
                _cleanup_free_ char *userns_fd_path = NULL;
88
0
                int r;
89
0
                if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
90
0
                        return -ENOMEM;
91
0
92
0
                r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
93
0
                if (r < 0)
94
0
                        return r;
95
0
                if (r)
96
0
                        userns_fd = -1;
97
0
        }
98
0
99
0
        if (pidns_fd >= 0)
100
0
                if (setns(pidns_fd, CLONE_NEWPID) < 0)
101
0
                        return -errno;
102
0
103
0
        if (mntns_fd >= 0)
104
0
                if (setns(mntns_fd, CLONE_NEWNS) < 0)
105
0
                        return -errno;
106
0
107
0
        if (netns_fd >= 0)
108
0
                if (setns(netns_fd, CLONE_NEWNET) < 0)
109
0
                        return -errno;
110
0
111
0
        if (userns_fd >= 0)
112
0
                if (setns(userns_fd, CLONE_NEWUSER) < 0)
113
0
                        return -errno;
114
0
115
0
        if (root_fd >= 0) {
116
0
                if (fchdir(root_fd) < 0)
117
0
                        return -errno;
118
0
119
0
                if (chroot(".") < 0)
120
0
                        return -errno;
121
0
        }
122
0
123
0
        return reset_uid_gid();
124
0
}
125
126
0
int fd_is_network_ns(int fd) {
127
0
        struct statfs s;
128
0
        int r;
129
0
130
0
        /* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice
131
0
         * way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle
132
0
         * this somewhat nicely.
133
0
         *
134
0
         * This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not
135
0
         * refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */
136
0
137
0
        if (fstatfs(fd, &s) < 0)
138
0
                return -errno;
139
0
140
0
        if (!is_fs_type(&s, NSFS_MAGIC)) {
141
0
                /* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs
142
0
                 * instead. Handle that in a somewhat smart way. */
143
0
144
0
                if (is_fs_type(&s, PROC_SUPER_MAGIC)) {
145
0
                        struct statfs t;
146
0
147
0
                        /* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the
148
0
                         * passed fd might refer to a network namespace, but we can't know for sure. In that case,
149
0
                         * return a recognizable error. */
150
0
151
0
                        if (statfs("/proc/self/ns/net", &t) < 0)
152
0
                                return -errno;
153
0
154
0
                        if (s.f_type == t.f_type)
155
0
                                return -EUCLEAN; /* It's possible, we simply don't know */
156
0
                }
157
0
158
0
                return 0; /* No! */
159
0
        }
160
0
161
0
        r = ioctl(fd, NS_GET_NSTYPE);
162
0
        if (r < 0) {
163
0
                if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */
164
0
                        return -EUCLEAN;
165
0
166
0
                return -errno;
167
0
        }
168
0
169
0
        return r == CLONE_NEWNET;
170
0
}