]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-container.c
Merge pull request #1833 from utezduyar/drop-warning-on-preset
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-container.c
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 <fcntl.h>
23 #include <unistd.h>
24
25 #include "bus-container.h"
26 #include "bus-internal.h"
27 #include "bus-socket.h"
28 #include "fd-util.h"
29 #include "process-util.h"
30 #include "util.h"
31
32 int bus_container_connect_socket(sd_bus *b) {
33 _cleanup_close_pair_ int pair[2] = { -1, -1 };
34 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
35 pid_t child;
36 siginfo_t si;
37 int r, error_buf = 0;
38 ssize_t n;
39
40 assert(b);
41 assert(b->input_fd < 0);
42 assert(b->output_fd < 0);
43 assert(b->nspid > 0 || b->machine);
44
45 if (b->nspid <= 0) {
46 r = container_get_leader(b->machine, &b->nspid);
47 if (r < 0)
48 return r;
49 }
50
51 r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
52 if (r < 0)
53 return r;
54
55 b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
56 if (b->input_fd < 0)
57 return -errno;
58
59 b->output_fd = b->input_fd;
60
61 bus_socket_setup(b);
62
63 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
64 return -errno;
65
66 child = fork();
67 if (child < 0)
68 return -errno;
69
70 if (child == 0) {
71 pid_t grandchild;
72
73 pair[0] = safe_close(pair[0]);
74
75 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
76 if (r < 0)
77 _exit(EXIT_FAILURE);
78
79 /* We just changed PID namespace, however it will only
80 * take effect on the children we now fork. Hence,
81 * let's fork another time, and connect from this
82 * grandchild, so that SO_PEERCRED of our connection
83 * comes from a process from within the container, and
84 * not outside of it */
85
86 grandchild = fork();
87 if (grandchild < 0)
88 _exit(EXIT_FAILURE);
89
90 if (grandchild == 0) {
91
92 r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
93 if (r < 0) {
94 /* Try to send error up */
95 error_buf = errno;
96 (void) write(pair[1], &error_buf, sizeof(error_buf));
97 _exit(EXIT_FAILURE);
98 }
99
100 _exit(EXIT_SUCCESS);
101 }
102
103 r = wait_for_terminate(grandchild, &si);
104 if (r < 0)
105 _exit(EXIT_FAILURE);
106
107 if (si.si_code != CLD_EXITED)
108 _exit(EXIT_FAILURE);
109
110 _exit(si.si_status);
111 }
112
113 pair[1] = safe_close(pair[1]);
114
115 r = wait_for_terminate(child, &si);
116 if (r < 0)
117 return r;
118
119 n = read(pair[0], &error_buf, sizeof(error_buf));
120 if (n < 0)
121 return -errno;
122
123 if (n > 0) {
124 if (n != sizeof(error_buf))
125 return -EIO;
126
127 if (error_buf < 0)
128 return -EIO;
129
130 if (error_buf == EINPROGRESS)
131 return 1;
132
133 if (error_buf > 0)
134 return -error_buf;
135 }
136
137 if (si.si_code != CLD_EXITED)
138 return -EIO;
139
140 if (si.si_status != EXIT_SUCCESS)
141 return -EIO;
142
143 return bus_socket_start_auth(b);
144 }
145
146 int bus_container_connect_kernel(sd_bus *b) {
147 _cleanup_close_pair_ int pair[2] = { -1, -1 };
148 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
149 union {
150 struct cmsghdr cmsghdr;
151 uint8_t buf[CMSG_SPACE(sizeof(int))];
152 } control = {};
153 int error_buf = 0;
154 struct iovec iov = {
155 .iov_base = &error_buf,
156 .iov_len = sizeof(error_buf),
157 };
158 struct msghdr mh = {
159 .msg_control = &control,
160 .msg_controllen = sizeof(control),
161 .msg_iov = &iov,
162 .msg_iovlen = 1,
163 };
164 struct cmsghdr *cmsg;
165 pid_t child;
166 siginfo_t si;
167 int r, fd = -1;
168 ssize_t n;
169
170 assert(b);
171 assert(b->input_fd < 0);
172 assert(b->output_fd < 0);
173 assert(b->nspid > 0 || b->machine);
174
175 if (b->nspid <= 0) {
176 r = container_get_leader(b->machine, &b->nspid);
177 if (r < 0)
178 return r;
179 }
180
181 r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
182 if (r < 0)
183 return r;
184
185 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
186 return -errno;
187
188 child = fork();
189 if (child < 0)
190 return -errno;
191
192 if (child == 0) {
193 pid_t grandchild;
194
195 pair[0] = safe_close(pair[0]);
196
197 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
198 if (r < 0)
199 _exit(EXIT_FAILURE);
200
201 /* We just changed PID namespace, however it will only
202 * take effect on the children we now fork. Hence,
203 * let's fork another time, and connect from this
204 * grandchild, so that kdbus only sees the credentials
205 * of this process which comes from within the
206 * container, and not outside of it */
207
208 grandchild = fork();
209 if (grandchild < 0)
210 _exit(EXIT_FAILURE);
211
212 if (grandchild == 0) {
213 fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
214 if (fd < 0) {
215 /* Try to send error up */
216 error_buf = errno;
217 (void) write(pair[1], &error_buf, sizeof(error_buf));
218 _exit(EXIT_FAILURE);
219 }
220
221 r = send_one_fd(pair[1], fd, 0);
222 if (r < 0)
223 _exit(EXIT_FAILURE);
224
225 _exit(EXIT_SUCCESS);
226 }
227
228 r = wait_for_terminate(grandchild, &si);
229 if (r < 0)
230 _exit(EXIT_FAILURE);
231
232 if (si.si_code != CLD_EXITED)
233 _exit(EXIT_FAILURE);
234
235 _exit(si.si_status);
236 }
237
238 pair[1] = safe_close(pair[1]);
239
240 r = wait_for_terminate(child, &si);
241 if (r < 0)
242 return r;
243
244 n = recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
245 if (n < 0)
246 return -errno;
247
248 CMSG_FOREACH(cmsg, &mh) {
249 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
250 int *fds;
251 unsigned n_fds;
252
253 assert(fd < 0);
254
255 fds = (int*) CMSG_DATA(cmsg);
256 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
257
258 if (n_fds != 1) {
259 close_many(fds, n_fds);
260 return -EIO;
261 }
262
263 fd = fds[0];
264 }
265 }
266
267 /* If there's an fd passed, we are good. */
268 if (fd >= 0) {
269 b->input_fd = b->output_fd = fd;
270 return bus_kernel_take_fd(b);
271 }
272
273 /* If there's an error passed, use it */
274 if (n == sizeof(error_buf) && error_buf > 0)
275 return -error_buf;
276
277 /* Otherwise, we have no clue */
278 return -EIO;
279 }