]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-container.c
util: introduce CMSG_FOREACH() macro and make use of it everywhere
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-container.c
CommitLineData
a7893c6b
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 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 <unistd.h>
23#include <fcntl.h>
24
25#include "util.h"
0b452006 26#include "process-util.h"
a7893c6b
LP
27#include "bus-internal.h"
28#include "bus-socket.h"
29#include "bus-container.h"
30
bc9fd78c 31int bus_container_connect_socket(sd_bus *b) {
a4475f57 32 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
ee502e0c 33 pid_t child;
bc9fd78c 34 siginfo_t si;
a7893c6b
LP
35 int r;
36
37 assert(b);
38 assert(b->input_fd < 0);
39 assert(b->output_fd < 0);
ee502e0c 40 assert(b->nspid > 0 || b->machine);
a7893c6b 41
ee502e0c
LP
42 if (b->nspid <= 0) {
43 r = container_get_leader(b->machine, &b->nspid);
44 if (r < 0)
45 return r;
46 }
a7893c6b 47
ee502e0c 48 r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &rootfd);
a7893c6b
LP
49 if (r < 0)
50 return r;
a7893c6b
LP
51
52 b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
53 if (b->input_fd < 0)
54 return -errno;
55
56 b->output_fd = b->input_fd;
57
8f04d2eb 58 bus_socket_setup(b);
a7893c6b
LP
59
60 child = fork();
61 if (child < 0)
62 return -errno;
63
64 if (child == 0) {
5d6cf65f 65 pid_t grandchild;
a7893c6b 66
878cd7e9 67 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
bc9fd78c 68 if (r < 0)
a7893c6b
LP
69 _exit(255);
70
5d6cf65f
LP
71 /* We just changed PID namespace, however it will only
72 * take effect on the children we now fork. Hence,
73 * let's fork another time, and connect from this
74 * grandchild, so that SO_PEERCRED of our connection
75 * comes from a process from within the container, and
76 * not outside of it */
a7893c6b 77
5d6cf65f
LP
78 grandchild = fork();
79 if (grandchild < 0)
a7893c6b 80 _exit(255);
5d6cf65f
LP
81
82 if (grandchild == 0) {
83
84 r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
85 if (r < 0) {
86 if (errno == EINPROGRESS)
87 _exit(1);
88
89 _exit(255);
90 }
91
92 _exit(EXIT_SUCCESS);
a7893c6b
LP
93 }
94
5d6cf65f
LP
95 r = wait_for_terminate(grandchild, &si);
96 if (r < 0)
97 _exit(255);
98
99 if (si.si_code != CLD_EXITED)
100 _exit(255);
101
102 _exit(si.si_status);
a7893c6b
LP
103 }
104
105 r = wait_for_terminate(child, &si);
106 if (r < 0)
107 return r;
108
109 if (si.si_code != CLD_EXITED)
110 return -EIO;
111
112 if (si.si_status == 1)
113 return 1;
114
bc9fd78c 115 if (si.si_status != EXIT_SUCCESS)
a7893c6b
LP
116 return -EIO;
117
118 return bus_socket_start_auth(b);
119}
bc9fd78c
LP
120
121int bus_container_connect_kernel(sd_bus *b) {
3d94f76c 122 _cleanup_close_pair_ int pair[2] = { -1, -1 };
a4475f57 123 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
bc9fd78c
LP
124 union {
125 struct cmsghdr cmsghdr;
126 uint8_t buf[CMSG_SPACE(sizeof(int))];
127 } control = {};
128 struct msghdr mh = {
129 .msg_control = &control,
130 .msg_controllen = sizeof(control),
131 };
132 struct cmsghdr *cmsg;
ee502e0c 133 pid_t child;
bc9fd78c
LP
134 siginfo_t si;
135 int r;
136 _cleanup_close_ int fd = -1;
137
138 assert(b);
139 assert(b->input_fd < 0);
140 assert(b->output_fd < 0);
ee502e0c 141 assert(b->nspid > 0 || b->machine);
bc9fd78c 142
ee502e0c
LP
143 if (b->nspid <= 0) {
144 r = container_get_leader(b->machine, &b->nspid);
145 if (r < 0)
146 return r;
147 }
bc9fd78c 148
ee502e0c 149 r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &rootfd);
bc9fd78c
LP
150 if (r < 0)
151 return r;
152
153 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
154 return -errno;
155
156 child = fork();
157 if (child < 0)
158 return -errno;
159
160 if (child == 0) {
5d6cf65f
LP
161 pid_t grandchild;
162
03e334a1 163 pair[0] = safe_close(pair[0]);
bc9fd78c 164
878cd7e9 165 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
bc9fd78c
LP
166 if (r < 0)
167 _exit(EXIT_FAILURE);
168
5d6cf65f
LP
169 /* We just changed PID namespace, however it will only
170 * take effect on the children we now fork. Hence,
171 * let's fork another time, and connect from this
172 * grandchild, so that kdbus only sees the credentials
173 * of this process which comes from within the
174 * container, and not outside of it */
175
176 grandchild = fork();
177 if (grandchild < 0)
bc9fd78c
LP
178 _exit(EXIT_FAILURE);
179
5d6cf65f
LP
180 if (grandchild == 0) {
181
182 fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
183 if (fd < 0)
184 _exit(EXIT_FAILURE);
185
186 cmsg = CMSG_FIRSTHDR(&mh);
187 cmsg->cmsg_level = SOL_SOCKET;
188 cmsg->cmsg_type = SCM_RIGHTS;
189 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
190 memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
191
192 mh.msg_controllen = cmsg->cmsg_len;
193
194 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
195 _exit(EXIT_FAILURE);
bc9fd78c 196
5d6cf65f
LP
197 _exit(EXIT_SUCCESS);
198 }
199
200 r = wait_for_terminate(grandchild, &si);
201 if (r < 0)
202 _exit(EXIT_FAILURE);
bc9fd78c 203
5d6cf65f 204 if (si.si_code != CLD_EXITED)
bc9fd78c
LP
205 _exit(EXIT_FAILURE);
206
5d6cf65f 207 _exit(si.si_status);
bc9fd78c
LP
208 }
209
03e334a1 210 pair[1] = safe_close(pair[1]);
bc9fd78c 211
fbadf045
LP
212 r = wait_for_terminate(child, &si);
213 if (r < 0)
214 return r;
215
216 if (si.si_code != CLD_EXITED)
217 return -EIO;
218
219 if (si.si_status != EXIT_SUCCESS)
220 return -EIO;
221
bc9fd78c
LP
222 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
223 return -errno;
224
2a1288ff 225 CMSG_FOREACH(cmsg, &mh)
bc9fd78c
LP
226 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
227 int *fds;
228 unsigned n_fds;
229
230 fds = (int*) CMSG_DATA(cmsg);
231 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
232
233 if (n_fds != 1) {
234 close_many(fds, n_fds);
235 return -EIO;
236 }
237
238 fd = fds[0];
239 }
240
bc9fd78c
LP
241 b->input_fd = b->output_fd = fd;
242 fd = -1;
243
244 return bus_kernel_take_fd(b);
245}